diff --git a/docs/resources/gno-interrealm.md b/docs/resources/gno-interrealm.md index ce615683e49..7c3873ddf6e 100644 --- a/docs/resources/gno-interrealm.md +++ b/docs/resources/gno-interrealm.md @@ -1,5 +1,25 @@ # Interrealm Specification +## Introduction + +XXX short intro on realms. +XXX comparison to kernel syscalls, but cross-user. +XXX simple code example. + +## Realm Finalization + +A realm boundary is defined as a change in realm in the call frame stack +from one realm to another, whether explicitly crossed with `cross(fn)()` +or implictly borrow-crossed into a different receiver's storage realm. +A realm may cross into itself with an explicit cross-call. + +When returning from a realm boundary, all new reachable objects are assigned +object IDs and stored in the current realm, ref-count-zero objects deleted +(full "disk-persistent cycle GC" will come after launch) and any modified +ref-count and Merkle hash root computed. This is called realm finalization. + +## `cross(fn)()` and `crossing()` + Gno extends Go's type system with interrealm rules. These rules can be checked during the static type-checking phase (but at the moment they are partially dependent on runtime checks). @@ -30,7 +50,8 @@ A crossing function declared in a realm different than the last explicitly crossed realm *must* be called like `cross(fn)(...)`. That is, functions of calls that result in explicit realm crossings must be wrapped with `cross()`. -`std.CurrentRealm()` returns the current realm last explicitly crossed to. +`std.CurrentRealm()` returns the current realm that was last explicitly crossed +to. `std.PreviousRealm()` returns the realm explicitly crossed to before that. @@ -59,7 +80,7 @@ receiver (and in general anything reachable is readable). New unreal objects reachable from the borrowed realm (or current realm if there was no method call that borrowed) become persisted in the borrowed realm (or current realm) upon finalization of the foreign object's method (or function). -(When you put an unlabeled photo in someone else's scrap book the photo now +(When you put an unlabeled photo in someone else's scrapbook the photo now belongs to the other person). In the future we will introduce an `attach()` function to prevent a new unreal object from being taken. @@ -72,7 +93,7 @@ A realm package's initialization (including `init()` calls) execute with current realm of itself, and it `std.PreviousRealm()` will panic unless the call stack includes a crossing function called like `cross(fn)(...)`. -### Justifications +### `cross` and `crossing` Design Goals P package code should behave the same even when copied verbatim in a realm package. @@ -107,7 +128,42 @@ be upgraded. Both `crossing()` and `cross(fn)(...)` statements may become special syntax in future Gno versions. -### Usage +## `attach()` + +## `panic()` and `revive(fn)` + +`panic()` behaves the same within the same realm boundary, but when a panic +crosses a realm boundary (as defined in [Realm +Finalization](#realm-finalization)) the Machine aborts the program. This is +because in a multi-user environment it isn't safe to let the caller recover +from realm panics that often leave the state in an invalid state. + +This would be sufficient, but we also want to write our tests to be able +to detect such aborts and make assertions. For this reason Gno provides +the `revive(fn)` builtin. + +```go +abort := revive(func() { + cross(func() { + crossing() + panic("cross-realm panic") + }) +}) +abort == "cross-realm panic" +``` + +`revive(fn)` will execute 'fn' and return the exception that crossed +a realm finalization boundary. + +This is only enabled in testing mode (for now), behavior is only partially +implemented. In the future `revive(fn)` will be available for non-testing code, +and the behavior will change such that `fn()` is run in transactional +(cache-wrapped) memory context and any mutations discarded if and only if there +was an abort. + +TL;DR: `revive(fn)` is Gno's builtin for STM (software transactional memory). + +## Application P package code cannot contain crossing functions, nor use `crossing()`. P package code also cannot import R realm packages. But code can call named @@ -126,8 +182,8 @@ realms. Generally you want your methods to be non-crossing. Because they should work for everyone. They are functions that are pre-bound to an object, and that -object is like a quasi-realm in itself, that could reside and migrate to other -realms possibly. This is consistent with any p code copied over to r realms; +object is like a quasi-realm in itself, that could possibly reside and migrate +to other realms. This is consistent with any p code copied over to r realms; none of those methods would be crossing, and behavior would be the same; stored in any realm, mostly non-crossing methods that anyone can call. Why is a quasi-realm self-encapsulated Object in need to modify the realm in which it is @@ -143,10 +199,10 @@ stdlibs functions are available unless overridden by the latter. `std.CurrentRealm()` shifts to `std.PreviousRealm()` if and only if a function is called like `cross(fn)(...)`. -#### MsgCall +### MsgCall MsgCall may only call crossing functions. This is to prevent potential -confusion of non-sophisticated users. Non-crossing calls of non-crossing +confusion for non-sophisticated users. Non-crossing calls of non-crossing functions of other realms is still possible with MsgRun. ```go @@ -180,7 +236,7 @@ func AnotherPublic() { } ``` -#### MsgRun +### MsgRun ```go // PKGPATH: gno.land/r/g1user/run @@ -259,12 +315,12 @@ Therefore in the MsgRun file's `init()` function the previous realm and current realm have different pkgpaths (the origin caller always has empty pkgpath) but the address is the same. -#### MsgAddPackage +### MsgAddPackage During MsgAddPackage `std.PreviousRealm()` refers to the package deployer both in global var decls as well as inside `init()` functions. After that the package deployer is no longer provided, so packages need to remember the -deployer in the initialization if needed. +deployer in the initialization phase if needed. ```go // PKGPATH: gno.land/r/test/test @@ -314,7 +370,7 @@ only times that `std.CurrentRealm()` will return a p package path that starts with "/p/" instead of "/r/". The package is technically still mutable during initialization. -#### Testing overrides with stdlibs/testing +### Testing overrides with stdlibs/testing The `gnovm/tests/stdlibs/testing/context_testing.gno` file provides functions for overriding frame details from Gno test code. @@ -419,7 +475,7 @@ func main() { // XXX ``` -#### Future Work +## Future Work `std.SetOriginCaller()` should maybe be deprecated in favor of `std.SetRealm(std.NewUserRealm(user))` renamed to diff --git a/examples/Makefile b/examples/Makefile index c11f3bffe42..2c2c03f5e32 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -34,7 +34,7 @@ build: .PHONY: test test: - go run ../gnovm/cmd/gno test -v ./... + go run ../gnovm/cmd/gno test -v ./gno.land/... .PHONY: test.fast test.fast: diff --git a/examples/gno.land/p/agherasie/forms/submit_test.gno b/examples/gno.land/p/agherasie/forms/submit_test.gno index c6883a75a30..68f57f69905 100644 --- a/examples/gno.land/p/agherasie/forms/submit_test.gno +++ b/examples/gno.land/p/agherasie/forms/submit_test.gno @@ -51,6 +51,7 @@ func TestAnswerForm(t *testing.T) { urequire.True(t, len(db.Answers) == 1, "Expected 1 answer, got", string(len(db.Answers))) urequire.True(t, db.Answers[0].FormID == formID, "Expected form ID", formID, "got", db.Answers[0].FormID) urequire.True(t, db.Answers[0].Answers == answers, "Expected answers", answers, "got", db.Answers[0].Answers) + urequire.True(t, err == nil, "Submit should not return an error") } func TestAnswerFormDates(t *testing.T) { diff --git a/examples/gno.land/p/demo/avl/z_0_filetest.gno b/examples/gno.land/p/demo/avl/z_0_filetest.gno index 8ba55bb3a0e..ec3dad63c4a 100644 --- a/examples/gno.land/p/demo/avl/z_0_filetest.gno +++ b/examples/gno.land/p/demo/avl/z_0_filetest.gno @@ -24,8 +24,6 @@ func main() { // Output: // false 2 - - // Realm: // finalizerealm["gno.land/r/test"] // u[a8ada09dee16d791fd406d629fe29bb0ed084a30:7]= diff --git a/examples/gno.land/p/demo/avl/z_1_filetest.gno b/examples/gno.land/p/demo/avl/z_1_filetest.gno index 8139816de7c..4b055be0a30 100644 --- a/examples/gno.land/p/demo/avl/z_1_filetest.gno +++ b/examples/gno.land/p/demo/avl/z_1_filetest.gno @@ -24,8 +24,6 @@ func main() { // Output: // false 3 - - // Realm: // finalizerealm["gno.land/r/test"] // u[a8ada09dee16d791fd406d629fe29bb0ed084a30:9]= diff --git a/examples/gno.land/p/demo/avl/z_2_filetest.gno b/examples/gno.land/p/demo/avl/z_2_filetest.gno index 2887216f535..8924310f01c 100644 --- a/examples/gno.land/p/demo/avl/z_2_filetest.gno +++ b/examples/gno.land/p/demo/avl/z_2_filetest.gno @@ -23,8 +23,6 @@ func main() { // Output: // false 3 - - // Realm: // finalizerealm["gno.land/r/test"] // u[a8ada09dee16d791fd406d629fe29bb0ed084a30:10]= diff --git a/examples/gno.land/p/demo/grc/grc20/tellers.gno b/examples/gno.land/p/demo/grc/grc20/tellers.gno index 9ec5bbe2d42..5a483a4dc3a 100644 --- a/examples/gno.land/p/demo/grc/grc20/tellers.gno +++ b/examples/gno.land/p/demo/grc/grc20/tellers.gno @@ -14,7 +14,7 @@ func (tok *Token) CallerTeller() Teller { return &fnTeller{ accountFn: func() std.Address { - caller := std.CurrentRealm().Address() + caller := std.PreviousRealm().Address() return caller }, Token: tok, diff --git a/examples/gno.land/p/demo/grc/grc20/tellers_test.gno b/examples/gno.land/p/demo/grc/grc20/tellers_test.gno index 08acba99952..06774a720ce 100644 --- a/examples/gno.land/p/demo/grc/grc20/tellers_test.gno +++ b/examples/gno.land/p/demo/grc/grc20/tellers_test.gno @@ -115,16 +115,21 @@ func TestCallerTeller(t *testing.T) { checkBalances(1000, 0, 0) checkAllowances(0, 0, 0, 0, 0, 0) + tellerThrough := func(action func()) { + testing.SetRealm(std.NewCodeRealm("gno.land/r/realm_exposing_the_teller")) + action() + } + testing.SetRealm(std.NewUserRealm(alice)) - urequire.NoError(t, teller.Approve(bob, 600)) + tellerThrough(func() { urequire.NoError(t, teller.Approve(bob, 600)) }) checkBalances(1000, 0, 0) checkAllowances(600, 0, 0, 0, 0, 0) testing.SetRealm(std.NewUserRealm(bob)) - urequire.Error(t, teller.TransferFrom(alice, carl, 700)) + tellerThrough(func() { urequire.Error(t, teller.TransferFrom(alice, carl, 700)) }) checkBalances(1000, 0, 0) checkAllowances(600, 0, 0, 0, 0, 0) - urequire.NoError(t, teller.TransferFrom(alice, carl, 400)) + tellerThrough(func() { urequire.NoError(t, teller.TransferFrom(alice, carl, 400)) }) checkBalances(600, 0, 400) checkAllowances(200, 0, 0, 0, 0, 0) } diff --git a/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable.gno b/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable.gno index 96a6a15f38b..a76936fa348 100644 --- a/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable.gno +++ b/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable.gno @@ -55,7 +55,17 @@ func (a *Authorizable) AddToAuthList(addr std.Address) error { if !a.OwnedByCurrent() { return ErrNotSuperuser } + return a.addToAuthList(addr) +} + +func (a *Authorizable) AddToAuthListByPrevious(addr std.Address) error { + if !a.OwnedByPrevious() { + return ErrNotSuperuser + } + return a.addToAuthList(addr) +} +func (a *Authorizable) addToAuthList(addr std.Address) error { if _, exists := a.authorized.Get(addr.String()); exists { return ErrAlreadyInList } @@ -69,7 +79,17 @@ func (a *Authorizable) DeleteFromAuthList(addr std.Address) error { if !a.OwnedByCurrent() { return ErrNotSuperuser } + return a.deleteFromAuthList(addr) +} +func (a *Authorizable) DeleteFromAuthListByPrevious(addr std.Address) error { + if !a.OwnedByPrevious() { + return ErrNotSuperuser + } + return a.deleteFromAuthList(addr) +} + +func (a *Authorizable) deleteFromAuthList(addr std.Address) error { if !a.authorized.Has(addr.String()) { return ErrNotInAuthList } @@ -82,20 +102,33 @@ func (a *Authorizable) DeleteFromAuthList(addr std.Address) error { return nil } -func (a Authorizable) CallerOnAuthList() error { - caller := std.CurrentRealm().Address() +func (a *Authorizable) OnAuthList() error { + current := std.CurrentRealm().Address() + return a.onAuthList(current) +} + +func (a *Authorizable) PreviousOnAuthList() error { + previous := std.PreviousRealm().Address() + return a.onAuthList(previous) +} +func (a *Authorizable) onAuthList(caller std.Address) error { if !a.authorized.Has(caller.String()) { return ErrNotInAuthList } - return nil } func (a Authorizable) AssertOnAuthList() { - caller := std.CurrentRealm().Address() + err := a.OnAuthList() + if err != nil { + panic(err) + } +} - if !a.authorized.Has(caller.String()) { - panic(ErrNotInAuthList) +func (a Authorizable) AssertPreviousOnAuthList() { + err := a.PreviousOnAuthList() + if err != nil { + panic(err) } } diff --git a/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable_test.gno b/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable_test.gno index 169488d0857..513a9b4be2f 100644 --- a/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable_test.gno +++ b/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable_test.gno @@ -35,20 +35,20 @@ func TestNewAuthorizableWithAddress(t *testing.T) { } } -func TestCallerOnAuthList(t *testing.T) { +func TestOnAuthList(t *testing.T) { a := NewAuthorizableWithAddress(alice) testing.SetRealm(std.NewUserRealm(alice)) - if err := a.CallerOnAuthList(); err == ErrNotInAuthList { + if err := a.OnAuthList(); err == ErrNotInAuthList { t.Fatalf("expected alice to be on the list") } } -func TestNotCallerOnAuthList(t *testing.T) { +func TestNotOnAuthList(t *testing.T) { a := NewAuthorizableWithAddress(alice) testing.SetRealm(std.NewUserRealm(bob)) - if err := a.CallerOnAuthList(); err == nil { + if err := a.OnAuthList(); err == nil { t.Fatalf("expected bob to not be on the list") } } diff --git a/examples/gno.land/p/demo/ownable/ownable_test.gno b/examples/gno.land/p/demo/ownable/ownable_test.gno index 2b68c2a9a3d..c7c3a66cb68 100644 --- a/examples/gno.land/p/demo/ownable/ownable_test.gno +++ b/examples/gno.land/p/demo/ownable/ownable_test.gno @@ -33,17 +33,13 @@ func TestNewWithOriginPanic(t *testing.T) { } func TestNewWithOrigin(t *testing.T) { - testing.SetOriginCaller(alice) testing.SetRealm(std.NewUserRealm(alice)) - - func() { + crossThrough(std.NewCodeRealm("gno.land/r/test/test"), func() { // This is the only way to test crosses from a p package for now. - testing.SetRealm(std.NewCodeRealm("gno.land/r/test/test")) - o := NewWithOrigin() got := o.Owner() uassert.Equal(t, got, alice) - }() + }) } func TestNewWithAddress(t *testing.T) { @@ -59,29 +55,29 @@ func TestTransferOwnership(t *testing.T) { o := New() err := o.TransferOwnership(bob) urequire.NoError(t, err) + got := o.Owner() uassert.Equal(t, got, bob) } func TestTransferOwnershipUnauthorized(t *testing.T) { - testing.SetOriginCaller(alice) - testing.SetRealm(std.NewUserRealm(alice)) - var o *Ownable - func() { - testing.SetRealm(std.NewCodeRealm("gno.land/r/test/test")) - o = NewWithOrigin() + testing.SetRealm(std.NewUserRealm(alice)) + crossThrough(std.NewCodeRealm("gno.land/r/test/test"), func() { + o = NewWithOrigin() // owned by alice + }) - // current is gno.land/r/test/test so of course errors. - uassert.ErrorContains(t, o.TransferOwnership(bob), ErrUnauthorized.Error()) + // Try unauthorized transfer from non-alice realm. + crossThrough(std.NewCodeRealm("gno.land/r/test/test"), func() { + uassert.ErrorContains(t, o.TransferOwnership(alice), ErrUnauthorized.Error()) uassert.ErrorContains(t, o.DropOwnership(), ErrUnauthorized.Error()) - }() + }) // Set realm to an unauthorized user bob. testing.SetRealm(std.NewUserRealm(bob)) - - uassert.ErrorContains(t, o.TransferOwnership(alice), ErrUnauthorized.Error()) + // current is gno.land/r/test/test so of course errors. + uassert.ErrorContains(t, o.TransferOwnership(bob), ErrUnauthorized.Error()) uassert.ErrorContains(t, o.DropOwnership(), ErrUnauthorized.Error()) // Reset realm to alice. @@ -92,7 +88,6 @@ func TestTransferOwnershipUnauthorized(t *testing.T) { func TestOwnedByCurrent(t *testing.T) { testing.SetRealm(std.NewUserRealm(alice)) - o := New() uassert.True(t, o.OwnedByCurrent()) } @@ -102,10 +97,9 @@ func TestOwnedByCurrentUnauthorized(t *testing.T) { testing.SetRealm(std.NewUserRealm(alice)) var o *Ownable - func() { - testing.SetRealm(std.NewCodeRealm("gno.land/r/test/test")) + crossThrough(std.NewCodeRealm("gno.land/r/test/test"), func() { o = NewWithOrigin() - }() + }) uassert.True(t, o.OwnedByCurrent()) @@ -116,24 +110,22 @@ func TestOwnedByCurrentUnauthorized(t *testing.T) { func TestOwnedByPrevious(t *testing.T) { testing.SetRealm(std.NewUserRealm(alice)) - o := New() - func() { - testing.SetRealm(std.NewCodeRealm("gno.land/r/test/test")) + + crossThrough(std.NewCodeRealm("gno.land/r/test/test"), func() { uassert.True(t, o.OwnedByPrevious()) - }() + }) } func TestOwnedByPreviousUnauthorized(t *testing.T) { testing.SetRealm(std.NewUserRealm(alice)) - o := New() + unauthorizedCaller := bob testing.SetRealm(std.NewUserRealm(unauthorizedCaller)) - func() { - testing.SetRealm(std.NewCodeRealm("gno.land/r/test/test")) + crossThrough(std.NewCodeRealm("gno.land/r/test/test"), func() { uassert.False(t, o.OwnedByPrevious()) - }() + }) } func TestDropOwnership(t *testing.T) { @@ -148,13 +140,10 @@ func TestDropOwnership(t *testing.T) { uassert.Empty(t, owner, "owner should be empty") } -// Errors - func TestErrInvalidAddress(t *testing.T) { testing.SetRealm(std.NewCodeRealm("gno.land/r/test/test")) o := New() - err := o.TransferOwnership("") uassert.ErrorContains(t, err, ErrInvalidAddress.Error()) @@ -181,10 +170,7 @@ func TestAssertOwnedByPrevious(t *testing.T) { testing.SetRealm(std.NewUserRealm(alice)) o := New() - - func() { - testing.SetRealm(std.NewCodeRealm("gno.land/r/test/test")) - + crossThrough(std.NewCodeRealm("gno.land/r/test/test"), func() { // Should not panic when previous is owner o.AssertOwnedByPrevious() @@ -193,7 +179,7 @@ func TestAssertOwnedByPrevious(t *testing.T) { uassert.PanicsWithMessage(t, ErrUnauthorized.Error(), func() { o.AssertOwnedByCurrent() }) - }() + }) } func TestNilReceiver(t *testing.T) { @@ -218,3 +204,8 @@ func TestNilReceiver(t *testing.T) { }() o.AssertOwnedByPrevious() } + +func crossThrough(rlm std.Realm, cr func()) { + testing.SetRealm(rlm) + cr() +} diff --git a/examples/gno.land/p/demo/uassert/helpers.gno b/examples/gno.land/p/demo/uassert/helpers.gno index bcea4f3b38f..25538e49b3b 100644 --- a/examples/gno.land/p/demo/uassert/helpers.gno +++ b/examples/gno.land/p/demo/uassert/helpers.gno @@ -14,13 +14,6 @@ func fail(t TestingT, customMsgs []string, failureMessage string, args ...any) b return false } -func autofail(t TestingT, success bool, customMsgs []string, failureMessage string, args ...any) bool { - if success { - return true - } - return fail(t, customMsgs, failureMessage, args...) -} - func checkDidPanic(f func()) (didPanic bool, message string) { didPanic = true defer func() { diff --git a/examples/gno.land/p/demo/uassert/mock_test.gno b/examples/gno.land/p/demo/uassert/mock_test.gno index fe7259cb57e..dede9fa4394 100644 --- a/examples/gno.land/p/demo/uassert/mock_test.gno +++ b/examples/gno.land/p/demo/uassert/mock_test.gno @@ -1,8 +1,10 @@ -package uassert +package uassert_test import ( "fmt" "testing" + + "gno.land/p/demo/uassert" ) type mockTestingT struct { @@ -12,7 +14,7 @@ type mockTestingT struct { // --- interface mock -var _ TestingT = (*mockTestingT)(nil) +var _ uassert.TestingT = (*mockTestingT)(nil) func (mockT *mockTestingT) Helper() { /* noop */ } func (mockT *mockTestingT) Skip(args ...any) { /* not implmented */ } diff --git a/examples/gno.land/p/demo/uassert/uassert.gno b/examples/gno.land/p/demo/uassert/uassert.gno index e256cdf0c6d..9877b73d764 100644 --- a/examples/gno.land/p/demo/uassert/uassert.gno +++ b/examples/gno.land/p/demo/uassert/uassert.gno @@ -79,8 +79,71 @@ func ErrorIs(t TestingT, err, target error, msgs ...string) bool { return true } -// PanicsWithMessage asserts that the code inside the specified func panics, -// and that the recovered panic value satisfies the given message +// AbortsWithMessage asserts that the code inside the specified func aborts +// (panics when crossing another realm). +// Use PanicsWithMessage for asserting local panics within the same realm. +// Note: This relies on gno's `revive` mechanism to catch aborts. +func AbortsWithMessage(t TestingT, msg string, f func(), msgs ...string) bool { + t.Helper() + + var didAbort bool + var abortValue any + + r := revive(f) // revive() captures the value passed to panic() + if r != nil { + didAbort = true + abortValue = r + } + + if !didAbort { + // If the function didn't abort as expected + return fail(t, msgs, "func should abort") + } + + // Check if the abort value matches the expected message string + abortStr, ok := abortValue.(string) + if !ok { + // Fail if the abort value wasn't a string + return fail(t, msgs, "abort value was not a string: got type %T, value %v", abortValue, abortValue) + } + + if abortStr != msg { + // Fail if the abort message doesn't match + return fail(t, msgs, "func should abort with message:\t%q\n\tActual abort value:\t%q", msg, abortStr) + } + + // Success: function aborted with the expected message + return true +} + +// NotAborts asserts that the code inside the specified func does NOT abort +// when crossing an execution boundary. +// Note: Consider using NotPanics which checks for both panics and aborts. +func NotAborts(t TestingT, f func(), msgs ...string) bool { + t.Helper() + + var didAbort bool + var abortValue any + + r := revive(f) // revive() captures the value passed to panic() + if r != nil { + didAbort = true + abortValue = r + } + + if didAbort { + // Fail if the function aborted when it shouldn't have + // Attempt to format the abort value in the error message + return fail(t, msgs, "func should not abort\n\tAbort value:\t%v", abortValue) + } + + // Success: function did not abort + return true +} + +// PanicsWithMessage asserts that the code inside the specified func panics +// locally within the same execution realm. +// Use AbortsWithMessage for asserting panics that cross execution boundaries (aborts). func PanicsWithMessage(t TestingT, msg string, f func(), msgs ...string) bool { t.Helper() @@ -90,21 +153,56 @@ func PanicsWithMessage(t TestingT, msg string, f func(), msgs ...string) bool { } if panicValue != msg { - return fail(t, msgs, "func should panic with message:\t%s\n\tPanic value:\t%s", msg, panicValue) + return fail(t, msgs, "func should panic with message:\t%q\n\tPanic value:\t%q", msg, panicValue) } return true } -// NotPanics asserts that the code inside the specified func does NOT panic. +// NotPanics asserts that the code inside the specified func does NOT panic +// (within the same realm) or abort (due to a cross-realm panic). func NotPanics(t TestingT, f func(), msgs ...string) bool { t.Helper() - didPanic, panicValue := checkDidPanic(f) + var panicVal any + var didPanic bool + var abortVal any + + // Use revive to catch cross-realm aborts + abortVal = revive(func() { + // Use defer+recover to catch same-realm panics + defer func() { + if r := recover(); r != nil { + didPanic = true + panicVal = r + } + }() + // Execute the function + f() + }) + + // Check if revive caught an abort + if abortVal != nil { + return fail(t, msgs, "func should not abort\n\tAbort value:\t%+v", abortVal) + } + // Check if recover caught a panic if didPanic { - return fail(t, msgs, "func should not panic\n\tPanic value:\t%s", panicValue) + // Format panic value for message + panicMsg := "" + if panicVal == nil { + panicMsg = "nil" + } else if err, ok := panicVal.(error); ok { + panicMsg = err.Error() + } else if str, ok := panicVal.(string); ok { + panicMsg = str + } else { + // Fallback for other types + panicMsg = "panic: unsupported type" + } + return fail(t, msgs, "func should not panic\n\tPanic value:\t%s", panicMsg) } - return true + + return true // No panic or abort occurred } // Equal asserts that two objects are equal. @@ -411,6 +509,7 @@ func isNumberEmpty(n any) (isNumber, isEmpty bool) { } return false, false } + func Empty(t TestingT, obj any, msgs ...string) bool { t.Helper() @@ -461,3 +560,42 @@ func NotEmpty(t TestingT, obj any, msgs ...string) bool { } return true } + +// Nil asserts that the value is nil. +func Nil(t TestingT, value any, msgs ...string) bool { + t.Helper() + if value != nil { + return fail(t, msgs, "should be nil") + } + return true +} + +// NotNil asserts that the value is not nil. +func NotNil(t TestingT, value any, msgs ...string) bool { + t.Helper() + if value == nil { + return fail(t, msgs, "should not be nil") + } + return true +} + +// TypedNil asserts that the value is a typed-nil (nil pointer) value. +func TypedNil(t TestingT, value any, msgs ...string) bool { + t.Helper() + if value == nil { + return fail(t, msgs, "should be typed-nil but got nil instead") + } + if !istypednil(value) { + return fail(t, msgs, "should be typed-nil") + } + return true +} + +// NotTypedNil asserts that the value is not a typed-nil (nil pointer) value. +func NotTypedNil(t TestingT, value any, msgs ...string) bool { + t.Helper() + if istypednil(value) { + return fail(t, msgs, "should not be typed-nil") + } + return true +} diff --git a/examples/gno.land/p/demo/uassert/uassert_test.gno b/examples/gno.land/p/demo/uassert/uassert_test.gno index 84283bf504c..b5569081291 100644 --- a/examples/gno.land/p/demo/uassert/uassert_test.gno +++ b/examples/gno.land/p/demo/uassert/uassert_test.gno @@ -1,38 +1,41 @@ -package uassert +package uassert_test import ( "errors" "fmt" "std" "testing" + + "gno.land/p/demo/uassert" + "gno.land/r/demo/tests" ) -var _ TestingT = (*testing.T)(nil) +var _ uassert.TestingT = (*testing.T)(nil) func TestMock(t *testing.T) { mockT := new(mockTestingT) mockT.empty(t) - NoError(mockT, errors.New("foo")) + uassert.NoError(mockT, errors.New("foo")) mockT.equals(t, "error: unexpected error: foo") - NoError(mockT, errors.New("foo"), "custom message") + uassert.NoError(mockT, errors.New("foo"), "custom message") mockT.equals(t, "error: unexpected error: foo - custom message") - NoError(mockT, errors.New("foo"), "custom", "message") + uassert.NoError(mockT, errors.New("foo"), "custom", "message") mockT.equals(t, "error: unexpected error: foo - custom message") } func TestNoError(t *testing.T) { mockT := new(mockTestingT) - True(t, NoError(mockT, nil)) + uassert.True(t, uassert.NoError(mockT, nil)) mockT.empty(t) - False(t, NoError(mockT, errors.New("foo bar"))) + uassert.False(t, uassert.NoError(mockT, errors.New("foo bar"))) mockT.equals(t, "error: unexpected error: foo bar") } func TestError(t *testing.T) { mockT := new(mockTestingT) - True(t, Error(mockT, errors.New("foo bar"))) + uassert.True(t, uassert.Error(mockT, errors.New("foo bar"))) mockT.empty(t) - False(t, Error(mockT, nil)) + uassert.False(t, uassert.Error(mockT, nil)) mockT.equals(t, "error: an error is expected but got nil") } @@ -41,16 +44,16 @@ func TestErrorContains(t *testing.T) { // nil error var err error - False(t, ErrorContains(mockT, err, ""), "ErrorContains should return false for nil arg") + uassert.False(t, uassert.ErrorContains(mockT, err, ""), "ErrorContains should return false for nil arg") } func TestTrue(t *testing.T) { mockT := new(mockTestingT) - if !True(mockT, true) { + if !uassert.True(mockT, true) { t.Error("True should return true") } mockT.empty(t) - if True(mockT, false) { + if uassert.True(mockT, false) { t.Error("True should return false") } mockT.equals(t, "error: should be true") @@ -58,11 +61,11 @@ func TestTrue(t *testing.T) { func TestFalse(t *testing.T) { mockT := new(mockTestingT) - if !False(mockT, false) { + if !uassert.False(mockT, false) { t.Error("False should return true") } mockT.empty(t) - if False(mockT, true) { + if uassert.False(mockT, true) { t.Error("False should return false") } mockT.equals(t, "error: should be false") @@ -70,50 +73,137 @@ func TestFalse(t *testing.T) { func TestPanicsWithMessage(t *testing.T) { mockT := new(mockTestingT) - if !PanicsWithMessage(mockT, "panic", func() { + if !uassert.PanicsWithMessage(mockT, "panic", func() { panic(errors.New("panic")) }) { t.Error("PanicsWithMessage should return true") } mockT.empty(t) - if PanicsWithMessage(mockT, "Panic!", func() { + if uassert.PanicsWithMessage(mockT, "Panic!", func() { // noop }) { t.Error("PanicsWithMessage should return false") } mockT.equals(t, "error: func should panic\n\tPanic value:\tnil") - if PanicsWithMessage(mockT, "at the disco", func() { + if uassert.PanicsWithMessage(mockT, "at the disco", func() { panic(errors.New("panic")) }) { t.Error("PanicsWithMessage should return false") } - mockT.equals(t, "error: func should panic with message:\tat the disco\n\tPanic value:\tpanic") + mockT.equals(t, "error: func should panic with message:\t\"at the disco\"\n\tPanic value:\t\"panic\"") - if PanicsWithMessage(mockT, "Panic!", func() { + if uassert.PanicsWithMessage(mockT, "Panic!", func() { panic("panic") }) { t.Error("PanicsWithMessage should return false") } - mockT.equals(t, "error: func should panic with message:\tPanic!\n\tPanic value:\tpanic") + mockT.equals(t, "error: func should panic with message:\t\"Panic!\"\n\tPanic value:\t\"panic\"") +} + +func TestAbortsWithMessage(t *testing.T) { + mockT := new(mockTestingT) + if !uassert.AbortsWithMessage(mockT, "abort message", func() { + cross(tests.ExecSwitch)(func() { + panic("abort message") + }) + panic("dontcare") + }) { + t.Error("AbortsWithMessage should return true") + } + mockT.empty(t) + + if uassert.AbortsWithMessage(mockT, "Abort!", func() { + // noop + }) { + t.Error("AbortsWithMessage should return false") + } + mockT.equals(t, "error: func should abort") + + if uassert.AbortsWithMessage(mockT, "at the disco", func() { + cross(tests.ExecSwitch)(func() { + panic("abort message") + }) + panic("dontcare") + }) { + t.Error("AbortsWithMessage should return false (wrong message)") + } + mockT.equals(t, "error: func should abort with message:\t\"at the disco\"\n\tActual abort value:\t\"abort message\"") + + // Test that non-crossing panics don't count as abort. + uassert.PanicsWithMessage(mockT, "non-abort panic", func() { + uassert.AbortsWithMessage(mockT, "dontcare2", func() { + panic("non-abort panic") + }) + t.Error("AbortsWithMessage should not have caught non-abort panic") + }, "non-abort panic") + mockT.empty(t) + + // Test case where abort value is not a string + if uassert.AbortsWithMessage(mockT, "doesn't matter", func() { + cross(tests.ExecSwitch)(func() { + panic(123) // abort with an integer + }) + panic("dontcare") + }) { + t.Error("AbortsWithMessage should return false when abort value is not a string") + } + mockT.equals(t, "error: abort value was not a string: got type int, value 123") + + // XXX: test with Error } func TestNotPanics(t *testing.T) { mockT := new(mockTestingT) - if !NotPanics(mockT, func() { + // Test case 1: No panic, no abort + if !uassert.NotPanics(mockT, func() { // noop }) { - t.Error("NotPanics should return true") + t.Error("NotPanics should return true when no panic/abort occurs") } mockT.empty(t) - if NotPanics(mockT, func() { + // Test case 2: Same-realm panic occurs + if uassert.NotPanics(mockT, func() { panic("Panic!") }) { - t.Error("NotPanics should return false") + t.Error("NotPanics should return false when same-realm panic occurs") + } + // Check the specific error message for same-realm panic + mockT.equals(t, "error: func should not panic\n\tPanic value:\tPanic!") + + // Test case 3: Cross-realm abort occurs + if uassert.NotPanics(mockT, func() { + cross(tests.ExecSwitch)(func() { + panic("Abort!") + }) + panic("dontcare") + }) { + t.Error("NotPanics should return false when cross-realm abort occurs") + } + // Check the specific error message for cross-realm abort + mockT.equals(t, "error: func should not abort\n\tAbort value:\tAbort!") + + // Test case 4: Panic with non-string/error + if uassert.NotPanics(mockT, func() { + panic(123) + }) { + t.Error("NotPanics should return false for non-string/error panic") + } + mockT.equals(t, "error: func should not panic\n\tPanic value:\tpanic: unsupported type") // Check formatted value + + // Test case 5: Abort with non-string/error + if uassert.NotPanics(mockT, func() { + cross(tests.ExecSwitch)(func() { + panic(true) + }) + }) { + t.Error("NotPanics should return false for non-string/error abort") } + mockT.equals(t, "error: func should not abort\n\tAbort value:\ttrue") // Check formatted value + } func TestEqual(t *testing.T) { @@ -148,7 +238,7 @@ func TestEqual(t *testing.T) { for _, c := range cases { name := fmt.Sprintf("Equal(%v, %v)", c.expected, c.actual) t.Run(name, func(t *testing.T) { - res := Equal(mockT, c.expected, c.actual) + res := uassert.Equal(mockT, c.expected, c.actual) if res != c.result { t.Errorf("%s should return %v: %s - %s", name, c.result, c.remark, mockT.actualString()) @@ -193,7 +283,7 @@ func TestNotEqual(t *testing.T) { for _, c := range cases { name := fmt.Sprintf("NotEqual(%v, %v)", c.expected, c.actual) t.Run(name, func(t *testing.T) { - res := NotEqual(mockT, c.expected, c.actual) + res := uassert.NotEqual(mockT, c.expected, c.actual) if res != c.result { t.Errorf("%s should return %v: %s - %s", name, c.result, c.remark, mockT.actualString()) @@ -239,7 +329,7 @@ func TestEmpty(t *testing.T) { for _, c := range cases { name := fmt.Sprintf("Empty(%v)", c.obj) t.Run(name, func(t *testing.T) { - res := Empty(mockT, c.obj) + res := uassert.Empty(mockT, c.obj) if res != c.expectedEmpty { t.Errorf("%s should return %v: %s", name, c.expectedEmpty, mockT.actualString()) @@ -310,7 +400,7 @@ func TestEqualWithStringDiff(t *testing.T) { for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { mockT := &mockTestingT{} - result := Equal(mockT, tc.expected, tc.actual) + result := uassert.Equal(mockT, tc.expected, tc.actual) if result != tc.shouldPass { t.Errorf("Expected Equal to return %v, but got %v", tc.shouldPass, result) @@ -357,7 +447,7 @@ func TestNotEmpty(t *testing.T) { for _, c := range cases { name := fmt.Sprintf("NotEmpty(%v)", c.obj) t.Run(name, func(t *testing.T) { - res := NotEmpty(mockT, c.obj) + res := uassert.NotEmpty(mockT, c.obj) if res != c.expectedNotEmpty { t.Errorf("%s should return %v: %s", name, c.expectedNotEmpty, mockT.actualString()) @@ -365,3 +455,67 @@ func TestNotEmpty(t *testing.T) { }) } } + +func TestNil(t *testing.T) { + mockT := new(mockTestingT) + if !uassert.Nil(mockT, nil) { + t.Error("Nil should return true") + } + mockT.empty(t) + if uassert.Nil(mockT, 0) { + t.Error("Nil should return false") + } + mockT.equals(t, "error: should be nil") + if uassert.Nil(mockT, (*int)(nil)) { + t.Error("Nil should return false") + } + mockT.equals(t, "error: should be nil") +} + +func TestNotNil(t *testing.T) { + mockT := new(mockTestingT) + if uassert.NotNil(mockT, nil) { + t.Error("NotNil should return false") + } + mockT.equals(t, "error: should not be nil") + if !uassert.NotNil(mockT, 0) { + t.Error("NotNil should return true") + } + mockT.empty(t) + if !uassert.NotNil(mockT, (*int)(nil)) { + t.Error("NotNil should return true") + } + mockT.empty(t) +} + +func TestTypedNil(t *testing.T) { + mockT := new(mockTestingT) + if uassert.TypedNil(mockT, nil) { + t.Error("TypedNil should return false") + } + mockT.equals(t, "error: should be typed-nil but got nil instead") + if uassert.TypedNil(mockT, 0) { + t.Error("TypedNil should return false") + } + mockT.equals(t, "error: should be typed-nil") + if !uassert.TypedNil(mockT, (*int)(nil)) { + t.Error("TypedNil should return true") + } + mockT.empty(t) +} + +func TestNotTypedNil(t *testing.T) { + mockT := new(mockTestingT) + if !uassert.NotTypedNil(mockT, nil) { + t.Error("NotTypedNil should return true") + } + mockT.empty(t) + if !uassert.NotTypedNil(mockT, 0) { + t.Error("NotTypedNil should return true") + } + mockT.empty(t) + if uassert.NotTypedNil(mockT, (*int)(nil)) { + t.Error("NotTypedNil should return false") + } + mockT.equals(t, "error: should not be typed-nil") +} diff --git a/examples/gno.land/p/demo/urequire/urequire.gno b/examples/gno.land/p/demo/urequire/urequire.gno index 5be1a62d9c1..18f3c62bca1 100644 --- a/examples/gno.land/p/demo/urequire/urequire.gno +++ b/examples/gno.land/p/demo/urequire/urequire.gno @@ -54,6 +54,33 @@ func ErrorIs(t uassert.TestingT, err, target error, msgs ...string) { t.FailNow() } +// AbortsWithMessage requires that the code inside the specified func aborts +// (panics when crossing another realm). +// Use PanicsWithMessage for requiring local panics within the same realm. +// Note: This relies on gno's `revive` mechanism to catch aborts. +func AbortsWithMessage(t uassert.TestingT, msg string, f func(), msgs ...string) { + t.Helper() + if uassert.AbortsWithMessage(t, msg, f, msgs...) { + return + } + t.FailNow() +} + +// NotAborts requires that the code inside the specified func does NOT abort +// when crossing an execution boundary (e.g., VM call). +// Use NotPanics for requiring the absence of local panics within the same realm. +// Note: This relies on Gno's `revive` mechanism. +func NotAborts(t uassert.TestingT, f func(), msgs ...string) { + t.Helper() + if uassert.NotPanics(t, f, msgs...) { + return + } + t.FailNow() +} + +// PanicsWithMessage requires that the code inside the specified func panics +// locally within the same execution realm. +// Use AbortsWithMessage for requiring panics that cross execution boundaries (aborts). func PanicsWithMessage(t uassert.TestingT, msg string, f func(), msgs ...string) { t.Helper() if uassert.PanicsWithMessage(t, msg, f, msgs...) { @@ -62,6 +89,9 @@ func PanicsWithMessage(t uassert.TestingT, msg string, f func(), msgs ...string) t.FailNow() } +// NotPanics requires that the code inside the specified func does NOT panic +// locally within the same execution realm. +// Use NotAborts for requiring the absence of panics that cross execution boundaries (aborts). func NotPanics(t uassert.TestingT, f func(), msgs ...string) { t.Helper() if uassert.NotPanics(t, f, msgs...) { diff --git a/examples/gno.land/p/demo/urequire/urequire_test.gno b/examples/gno.land/p/demo/urequire/urequire_test.gno index 463e3fffc07..6668879fdf6 100644 --- a/examples/gno.land/p/demo/urequire/urequire_test.gno +++ b/examples/gno.land/p/demo/urequire/urequire_test.gno @@ -4,5 +4,7 @@ import "testing" func TestPackage(t *testing.T) { Equal(t, 42, 42) - // XXX: find a way to unit test this package + + // XXX: find a way to unit test this package thoroughly, + // especially the t.FailNow() behavior on assertion failure. } diff --git a/examples/gno.land/p/moul/authz/authz.gno b/examples/gno.land/p/moul/authz/authz.gno index 267e70b65e3..9e4bc2ba490 100644 --- a/examples/gno.land/p/moul/authz/authz.gno +++ b/examples/gno.land/p/moul/authz/authz.gno @@ -36,6 +36,7 @@ package authz import ( "errors" "std" + "strings" "gno.land/p/demo/avl" "gno.land/p/demo/avl/rotree" @@ -44,12 +45,15 @@ import ( "gno.land/p/moul/once" ) -// Authorizer is the main wrapper object that handles authority management +// Authorizer is the main wrapper object that handles authority management. +// It is configured with a replaceable Authority implementation. type Authorizer struct { current Authority } -// Authority represents an entity that can authorize privileged actions +// Authority represents an entity that can authorize privileged actions. +// It is implemented by MemberAuthority, ContractAuthority, AutoAcceptAuthority, +// and DroppedAuthority. 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) @@ -66,13 +70,41 @@ type PrivilegedAction func() error // 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 { +// NewWithCurrent creates a new Authorizer with the current realm's address as authority +func NewWithCurrent() *Authorizer { return &Authorizer{ current: NewMemberAuthority(std.CurrentRealm().Address()), } } +// NewWithPrevious creates a new Authorizer with the previous realm's address as authority +func NewWithPrevious() *Authorizer { + return &Authorizer{ + current: NewMemberAuthority(std.PreviousRealm().Address()), + } +} + +// NewWithCurrent creates a new Authorizer with the current realm's address as authority +func NewWithMembers(addrs ...std.Address) *Authorizer { + return &Authorizer{ + current: NewMemberAuthority(addrs...), + } +} + +// NewWithOrigin creates a new Authorizer with the origin caller's address as +// authority. +// This is typically used in the init() function. +func NewWithOrigin() *Authorizer { + origin := std.OriginCaller() + previous := std.PreviousRealm() + if origin != previous.Address() { + panic("NewWithOrigin() should be called from init() where std.PreviousRealm() is origin") + } + return &Authorizer{ + current: NewMemberAuthority(origin), + } +} + // NewWithAuthority creates a new Authorizer with a specific authority func NewWithAuthority(authority Authority) *Authorizer { return &Authorizer{ @@ -101,10 +133,22 @@ func (a *Authorizer) Do(title string, action PrivilegedAction, args ...any) erro // String returns a string representation of the current authority func (a *Authorizer) String() string { - return a.current.String() + currentStr := a.current.String() + + switch a.current.(type) { + case *MemberAuthority: + case *ContractAuthority: + case *AutoAcceptAuthority: + case *droppedAuthority: + default: + // this way official "dropped" is different from "*custom*: dropped" (autoclaimed). + return ufmt.Sprintf("custom_authority[%s]", currentStr) + } + return currentStr } -// MemberAuthority is the default implementation using addrset for member management +// MemberAuthority is the default implementation using addrset for member +// management. type MemberAuthority struct { members addrset.Set } @@ -130,13 +174,13 @@ func (a *MemberAuthority) Authorize(title string, action PrivilegedAction, args } func (a *MemberAuthority) String() string { - out := "" + addrs := []string{} a.members.Tree().Iterate("", "", func(key string, _ any) bool { - out += "- " + key + "\n" + addrs = append(addrs, key) return false }) - - return out + addrsStr := strings.Join(addrs, ",") + return ufmt.Sprintf("member_authority[%s]", addrsStr) } // AddMember adds a new member to the authority @@ -193,7 +237,8 @@ func NewContractAuthority(path string, handler PrivilegedActionHandler) *Contrac } } -// NewRestrictedContractAuthority creates a new contract authority with a proposer restriction +// 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") @@ -245,7 +290,8 @@ func (a *ContractAuthority) String() string { } // AutoAcceptAuthority implements an authority that accepts all actions -// AutoAcceptAuthority is a simple authority that automatically 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{} diff --git a/examples/gno.land/p/moul/authz/authz_test.gno b/examples/gno.land/p/moul/authz/authz_test.gno index a4a7369d2e5..4a5867a2f4f 100644 --- a/examples/gno.land/p/moul/authz/authz_test.gno +++ b/examples/gno.land/p/moul/authz/authz_test.gno @@ -10,11 +10,11 @@ import ( "gno.land/p/demo/uassert" ) -func TestNew(t *testing.T) { +func TestNewWithCurrent(t *testing.T) { testAddr := testutils.TestAddress("alice") - testing.SetOriginCaller(testAddr) + testing.SetRealm(std.NewUserRealm(testAddr)) - auth := New() + auth := NewWithCurrent() // Check that the current authority is a MemberAuthority memberAuth, ok := auth.Current().(*MemberAuthority) @@ -39,9 +39,9 @@ func TestNewWithAuthority(t *testing.T) { func TestAuthorizerAuthorize(t *testing.T) { testAddr := testutils.TestAddress("alice") - testing.SetOriginCaller(testAddr) + testing.SetRealm(std.NewUserRealm(testAddr)) - auth := New() + auth := NewWithCurrent() // Test successful action with args executed := false @@ -55,7 +55,7 @@ func TestAuthorizerAuthorize(t *testing.T) { uassert.True(t, executed, "action should have been executed") // Test unauthorized action with args - testing.SetOriginCaller(testutils.TestAddress("bob")) + testing.SetRealm(std.NewUserRealm(testutils.TestAddress("bob"))) executed = false err = auth.Do("test_action", func() error { @@ -67,7 +67,7 @@ func TestAuthorizerAuthorize(t *testing.T) { uassert.False(t, executed, "action should not have been executed") // Test action returning error - testing.SetOriginCaller(testAddr) + testing.SetRealm(std.NewUserRealm(testAddr)) expectedErr := errors.New("test error") err = auth.Do("test_action", func() error { @@ -79,9 +79,9 @@ func TestAuthorizerAuthorize(t *testing.T) { func TestAuthorizerTransfer(t *testing.T) { testAddr := testutils.TestAddress("alice") - testing.SetOriginCaller(testAddr) + testing.SetRealm(std.NewUserRealm(testAddr)) - auth := New() + auth := NewWithCurrent() // Test transfer to new member authority newAddr := testutils.TestAddress("bob") @@ -92,13 +92,13 @@ func TestAuthorizerTransfer(t *testing.T) { uassert.True(t, auth.Current() == newAuth, "expected new authority") // Test unauthorized transfer - testing.SetOriginCaller(testutils.TestAddress("carol")) + testing.SetRealm(std.NewUserRealm(testutils.TestAddress("carol"))) err = auth.Transfer(NewMemberAuthority(testAddr)) uassert.True(t, err != nil, "expected error") // Test transfer to contract authority - testing.SetOriginCaller(newAddr) + testing.SetRealm(std.NewUserRealm(newAddr)) contractAuth := NewContractAuthority("gno.land/r/test", func(title string, action PrivilegedAction) error { return action() @@ -111,10 +111,10 @@ func TestAuthorizerTransfer(t *testing.T) { func TestAuthorizerTransferChain(t *testing.T) { testAddr := testutils.TestAddress("alice") - testing.SetOriginCaller(testAddr) + testing.SetRealm(std.NewUserRealm(testAddr)) // Create a chain of transfers - auth := New() + auth := NewWithCurrent() // First transfer to a new member authority newAddr := testutils.TestAddress("bob") @@ -128,14 +128,14 @@ func TestAuthorizerTransferChain(t *testing.T) { return action() }) - testing.SetOriginCaller(newAddr) + testing.SetRealm(std.NewUserRealm(newAddr)) err = auth.Transfer(contractAuth) uassert.True(t, err == nil, "unexpected error in second transfer") // Finally transfer to an auto-accept authority autoAuth := NewAutoAcceptAuthority() - testing.SetOriginCaller(std.DerivePkgAddr("gno.land/r/test")) + testing.SetRealm(std.NewCodeRealm("gno.land/r/test")) err = auth.Transfer(autoAuth) uassert.True(t, err == nil, "unexpected error in final transfer") uassert.True(t, auth.Current() == autoAuth, "expected auto-accept authority") @@ -143,9 +143,9 @@ func TestAuthorizerTransferChain(t *testing.T) { func TestAuthorizerWithDroppedAuthority(t *testing.T) { testAddr := testutils.TestAddress("alice") - testing.SetOriginCaller(testAddr) + testing.SetRealm(std.NewUserRealm(testAddr)) - auth := New() + auth := NewWithCurrent() // Transfer to dropped authority err := auth.Transfer(NewDroppedAuthority()) @@ -182,7 +182,7 @@ func TestContractAuthorityHandlerExecutionOnce(t *testing.T) { }) // Set caller to contract address - testing.SetOriginCaller(std.DerivePkgAddr("gno.land/r/test")) + testing.SetRealm(std.NewCodeRealm("gno.land/r/test")) testArgs := []any{"proposal_id", 42, "metadata", map[string]string{"key": "value"}} err := contractAuth.Authorize("test_action", func() error { @@ -203,7 +203,7 @@ func TestContractAuthorityExecutionTwice(t *testing.T) { }) // Set caller to contract address - testing.SetOriginCaller(std.DerivePkgAddr("gno.land/r/test")) + testing.SetRealm(std.NewCodeRealm("gno.land/r/test")) testArgs := []any{"proposal_id", 42, "metadata", map[string]string{"key": "value"}} @@ -235,12 +235,12 @@ func TestContractAuthorityWithProposer(t *testing.T) { contractAuth := NewRestrictedContractAuthority("gno.land/r/test", func(title string, action PrivilegedAction) error { handlerCalled = true // Set caller to contract address before executing action - testing.SetOriginCaller(std.DerivePkgAddr("gno.land/r/test")) + testing.SetRealm(std.NewCodeRealm("gno.land/r/test")) return action() }, memberAuth) // Test authorized member - testing.SetOriginCaller(testAddr) + testing.SetRealm(std.NewUserRealm(testAddr)) testArgs := []any{"proposal_metadata", "test value"} err := contractAuth.Authorize("test_action", func() error { actionExecuted = true @@ -256,7 +256,7 @@ func TestContractAuthorityWithProposer(t *testing.T) { actionExecuted = false // Test unauthorized proposer - testing.SetOriginCaller(testutils.TestAddress("bob")) + testing.SetRealm(std.NewUserRealm(testutils.TestAddress("bob"))) err = contractAuth.Authorize("test_action", func() error { actionExecuted = true return nil @@ -281,7 +281,7 @@ func TestAutoAcceptAuthority(t *testing.T) { uassert.True(t, executed, "action should have been executed") // Test with different caller - testing.SetOriginCaller(testutils.TestAddress("random")) + testing.SetRealm(std.NewUserRealm(testutils.TestAddress("random"))) executed = false err = auth.Authorize("test_action", func() error { executed = true @@ -317,7 +317,7 @@ func TestMemberAuthorityMultipleMembers(t *testing.T) { // Test that both members can execute actions for _, member := range []std.Address{alice, bob} { - testing.SetOriginCaller(member) + testing.SetRealm(std.NewUserRealm(member)) err := auth.Authorize("test_action", func() error { return nil }) @@ -325,7 +325,7 @@ func TestMemberAuthorityMultipleMembers(t *testing.T) { } // Test that non-member cannot execute - testing.SetOriginCaller(carol) + testing.SetRealm(std.NewUserRealm(carol)) err := auth.Authorize("test_action", func() error { return nil }) @@ -354,7 +354,7 @@ func TestMemberAuthorityMultipleMembers(t *testing.T) { } func TestAuthorizerCurrentNeverNil(t *testing.T) { - auth := New() + auth := NewWithCurrent() // Current should never be nil after initialization uassert.True(t, auth.Current() != nil, "current authority should not be nil") @@ -365,48 +365,144 @@ func TestAuthorizerCurrentNeverNil(t *testing.T) { uassert.True(t, auth.Current() != nil, "current authority should not be nil after transfer") } +func TestContractAuthorityValidation(t *testing.T) { + /* + // Test empty path - should panic + panicked := false + func() { + defer func() { + if r := recover(); r != nil { + panicked = true + } + }() + NewContractAuthority("", nil) + }() + uassert.True(t, panicked, "expected panic for empty path") + */ + + // Test nil handler - should return error on Authorize + auth := NewContractAuthority("gno.land/r/test", nil) + testing.SetRealm(std.NewCodeRealm("gno.land/r/test")) + err := auth.Authorize("test", func() error { + return nil + }) + uassert.True(t, err != nil, "nil handler authority should fail to authorize") + + // Test valid configuration + handler := func(title string, action PrivilegedAction) error { + return nil + } + contractAuth := NewContractAuthority("gno.land/r/test", handler) + testing.SetRealm(std.NewCodeRealm("gno.land/r/test")) + err = contractAuth.Authorize("test", func() error { + return nil + }) + uassert.True(t, err == nil, "valid contract authority should authorize successfully") +} + func TestAuthorizerString(t *testing.T) { - auth := New() + auth := NewWithCurrent() // Test initial string representation str := auth.String() - uassert.True(t, str != "", "string representation should not be empty") + uassert.Equal(t, str, "member_authority[g134ru6z8r00teg3r342h3yqf9y55mztdvlj4758]") // Test string after transfer autoAuth := NewAutoAcceptAuthority() err := auth.Transfer(autoAuth) uassert.True(t, err == nil, "transfer should succeed") + str = auth.String() + uassert.Equal(t, str, "auto_accept_authority") - newStr := auth.String() - uassert.True(t, newStr != "", "string representation should not be empty") - uassert.True(t, newStr != str, "string should change after transfer") + // Test custom authority + customAuth := &mockAuthority{} + err = auth.Transfer(customAuth) + uassert.True(t, err == nil, "transfer should succeed") + str = auth.String() + uassert.Equal(t, str, "custom_authority[mock]") } -func TestContractAuthorityValidation(t *testing.T) { - // Test empty path - auth := NewContractAuthority("", nil) - testing.SetOriginCaller(std.DerivePkgAddr("")) - err := auth.Authorize("test", func() error { - return nil - }) - uassert.True(t, err != nil, "empty path authority should fail to authorize") +type mockAuthority struct{} - // Test nil handler - auth = NewContractAuthority("gno.land/r/test", nil) - testing.SetOriginCaller(std.DerivePkgAddr("gno.land/r/test")) - err = auth.Authorize("test", func() error { - return nil - }) - uassert.True(t, err != nil, "nil handler authority should fail to authorize") +func (c mockAuthority) String() string { return "mock" } +func (a mockAuthority) Authorize(title string, action PrivilegedAction, args ...any) error { + // autoaccept + return action() +} - // Test valid configuration - handler := func(title string, action PrivilegedAction) error { - return nil +func TestAuthorityString(t *testing.T) { + testAddr := testutils.TestAddress("alice") + + // MemberAuthority + memberAuth := NewMemberAuthority(testAddr) + memberStr := memberAuth.String() + expectedMemberStr := "member_authority[g1v9kxjcm9ta047h6lta047h6lta047h6lzd40gh]" + uassert.Equal(t, memberStr, expectedMemberStr) + + // ContractAuthority + contractAuth := NewContractAuthority("gno.land/r/test", func(title string, action PrivilegedAction) error { return nil }) + contractStr := contractAuth.String() + expectedContractStr := "contract_authority[contract=gno.land/r/test]" + uassert.Equal(t, contractStr, expectedContractStr) + + // AutoAcceptAuthority + autoAuth := NewAutoAcceptAuthority() + autoStr := autoAuth.String() + expectedAutoStr := "auto_accept_authority" + uassert.Equal(t, autoStr, expectedAutoStr) + + // DroppedAuthority + droppedAuth := NewDroppedAuthority() + droppedStr := droppedAuth.String() + expectedDroppedStr := "dropped_authority" + uassert.Equal(t, droppedStr, expectedDroppedStr) +} + +func TestContractAuthorityUnauthorizedCaller(t *testing.T) { + contractPath := "gno.land/r/testcontract" + contractAddr := std.DerivePkgAddr(contractPath) + unauthorizedAddr := testutils.TestAddress("unauthorized") + + // Handler that checks the caller before proceeding + handlerExecutedCorrectly := false // Tracks if handler logic ran correctly + handlerErrorMsg := "handler: caller is not the contract" + contractHandler := func(title string, action PrivilegedAction) error { + caller := std.CurrentRealm().Address() + if caller != contractAddr { + return errors.New(handlerErrorMsg) + } + // Only execute action and mark success if caller is correct + handlerExecutedCorrectly = true + return action() } - contractAuth := NewContractAuthority("gno.land/r/test", handler) - testing.SetOriginCaller(std.DerivePkgAddr("gno.land/r/test")) - err = contractAuth.Authorize("test", func() error { + + contractAuth := NewContractAuthority(contractPath, contractHandler) + authorizer := NewWithAuthority(contractAuth) // Start with ContractAuthority + + actionExecuted := false + privilegedAction := func() error { + actionExecuted = true return nil - }) - uassert.True(t, err == nil, "valid contract authority should authorize successfully") + } + + // 1. Attempt action from unauthorized user + testing.SetRealm(std.NewUserRealm(unauthorizedAddr)) + err := authorizer.Do("test_action_unauthorized", privilegedAction) + + // Assertions for unauthorized call + uassert.Error(t, err, "Do should return an error for unauthorized caller") + uassert.ErrorContains(t, err, handlerErrorMsg, "Error should originate from the handler check") + uassert.False(t, handlerExecutedCorrectly, "Handler should not have executed successfully for unauthorized caller") + uassert.False(t, actionExecuted, "Privileged action should not have executed for unauthorized caller") + + // 2. Attempt action from the correct contract + handlerExecutedCorrectly = false // Reset flag + actionExecuted = false // Reset flag + testing.SetRealm(std.NewCodeRealm(contractPath)) + err = authorizer.Do("test_action_authorized", privilegedAction) + + // Assertions for authorized call + uassert.NoError(t, err, "Do should succeed for authorized contract caller") + uassert.True(t, handlerExecutedCorrectly, "Handler should have executed successfully for authorized caller") + uassert.True(t, actionExecuted, "Privileged action should have executed for authorized caller") } diff --git a/examples/gno.land/p/moul/authz/example_test.gno b/examples/gno.land/p/moul/authz/example_test.gno index a283204dfc2..80862cef2d6 100644 --- a/examples/gno.land/p/moul/authz/example_test.gno +++ b/examples/gno.land/p/moul/authz/example_test.gno @@ -7,7 +7,7 @@ import ( // Example_basic demonstrates initializing and using a basic member authority func Example_basic() { // Initialize with contract deployer as authority - auth := New() + auth := NewWithOrigin() // Use the authority to perform a privileged action auth.Do("update_config", func() error { @@ -19,7 +19,7 @@ func Example_basic() { // Example_addingMembers demonstrates how to add new members to a member authority func Example_addingMembers() { // Initialize with contract deployer as authority - auth := New() + auth := NewWithCurrent() // Add a new member to the authority memberAuth := auth.Current().(*MemberAuthority) @@ -72,7 +72,7 @@ func Example_restrictedContractAuthority() { // Example_switchingAuthority demonstrates switching from member to contract authority func Example_switchingAuthority() { // Start with member authority (deployer) - auth := New() + auth := NewWithCurrent() // Create and switch to contract authority daoAuthority := NewContractAuthority( diff --git a/examples/gno.land/p/n2p5/loci/loci.gno b/examples/gno.land/p/n2p5/loci/loci.gno index b0849b3ece4..a50c537a8f4 100644 --- a/examples/gno.land/p/n2p5/loci/loci.gno +++ b/examples/gno.land/p/n2p5/loci/loci.gno @@ -25,10 +25,10 @@ func New() *LociStore { } } -// Set stores a byte slice in the AVL tree using the `std.CurrentRealm().Address()` +// Set stores a byte slice in the AVL tree using the `std.PreviousRealm().Address()` // string as the key. func (s *LociStore) Set(value []byte) { - key := string(std.CurrentRealm().Address()) + key := string(std.PreviousRealm().Address()) s.internal.Set(key, value) } diff --git a/examples/gno.land/p/n2p5/loci/loci_test.gno b/examples/gno.land/p/n2p5/loci/loci_test.gno index 3e96ed4d425..18d1202e8d6 100644 --- a/examples/gno.land/p/n2p5/loci/loci_test.gno +++ b/examples/gno.land/p/n2p5/loci/loci_test.gno @@ -1,79 +1,40 @@ package loci import ( + "std" "testing" "gno.land/p/demo/testutils" ) func TestLociStore(t *testing.T) { - //testing.SetRealm(std.NewCodeRealm("gno.land/r/test/test")) - t.Run("TestSet", func(t *testing.T) { - t.Parallel() + testing.SetRealm(std.NewCodeRealm("gno.land/r/test/test")) + caller := std.PreviousRealm() store := New() - u1 := testutils.TestAddress("u1") - - m1 := []byte("hello") - m2 := []byte("world") - testing.SetOriginCaller(u1) - // Ensure that the value is nil before setting it. - r1 := store.Get(u1) - if r1 != nil { + if r1 := store.Get(caller.Address()); r1 != nil { t.Errorf("expected value to be nil, got '%s'", r1) } - store.Set(m1) - // Ensure that the value is correct after setting it. - r2 := store.Get(u1) - if string(r2) != "hello" { + store.Set([]byte("hello")) + if r2 := store.Get(caller.Address()); string(r2) != "hello" { t.Errorf("expected value to be 'hello', got '%s'", r2) } - store.Set(m2) - // Ensure that the value is correct after overwriting it. - r3 := store.Get(u1) - if string(r3) != "world" { + store.Set([]byte("world")) + if r3 := store.Get(caller.Address()); string(r3) != "world" { t.Errorf("expected value to be 'world', got '%s'", r3) } }) t.Run("TestGet", func(t *testing.T) { - t.Parallel() + testing.SetRealm(std.NewCodeRealm("gno.land/r/test/test")) + caller := std.PreviousRealm() store := New() - u1 := testutils.TestAddress("u1") - u2 := testutils.TestAddress("u2") - u3 := testutils.TestAddress("u3") - u4 := testutils.TestAddress("u4") - - m1 := []byte("hello") - m2 := []byte("world") - m3 := []byte("goodbye") - - testing.SetOriginCaller(u1) - store.Set(m1) - testing.SetOriginCaller(u2) - store.Set(m2) - testing.SetOriginCaller(u3) - store.Set(m3) - - // Ensure that the value is correct after setting it. - r0 := store.Get(u4) - if r0 != nil { + store.Set([]byte("hello")) + if r0 := store.Get(testutils.TestAddress("nil_user")); r0 != nil { t.Errorf("expected value to be nil, got '%s'", r0) } - // Ensure that the value is correct after setting it. - r1 := store.Get(u1) - if string(r1) != "hello" { + if r1 := store.Get(caller.Address()); string(r1) != "hello" { t.Errorf("expected value to be 'hello', got '%s'", r1) } - // Ensure that the value is correct after setting it. - r2 := store.Get(u2) - if string(r2) != "world" { - t.Errorf("expected value to be 'world', got '%s'", r2) - } - // Ensure that the value is correct after setting it. - r3 := store.Get(u3) - if string(r3) != "goodbye" { - t.Errorf("expected value to be 'goodbye', got '%s'", r3) - } }) } diff --git a/examples/gno.land/p/n2p5/loci/z_0_filetest.gno b/examples/gno.land/p/n2p5/loci/z_0_filetest.gno new file mode 100644 index 00000000000..4c59905a4da --- /dev/null +++ b/examples/gno.land/p/n2p5/loci/z_0_filetest.gno @@ -0,0 +1,24 @@ +// PKGPATH: gno.land/r/test +package test + +import ( + "std" + + "gno.land/p/n2p5/loci" +) + +var store *loci.LociStore + +func init() { + store = loci.New() +} + +func main() { + caller := std.PreviousRealm() + + store.Set([]byte("hello, world")) + println(string(store.Get(caller.Address()))) +} + +// Output: +// hello, world diff --git a/examples/gno.land/r/demo/atomicswap/atomicswap.gno b/examples/gno.land/r/demo/atomicswap/atomicswap.gno index 90e0970bb48..6ba631a981a 100644 --- a/examples/gno.land/r/demo/atomicswap/atomicswap.gno +++ b/examples/gno.land/r/demo/atomicswap/atomicswap.gno @@ -114,7 +114,7 @@ func NewCustomGRC20Swap(recipient std.Address, hashlock string, timelock time.Ti allowance := token.Allowance(sender, curAddr) require(allowance > 0, "no allowance") - userTeller := token.CallerTeller() + userTeller := token.RealmTeller() err := userTeller.TransferFrom(sender, curAddr, allowance) require(err == nil, "cannot retrieve tokens from allowance") diff --git a/examples/gno.land/r/demo/atomicswap/atomicswap_test.gno b/examples/gno.land/r/demo/atomicswap/atomicswap_test.gno index 6547642670f..2e051091b7d 100644 --- a/examples/gno.land/r/demo/atomicswap/atomicswap_test.gno +++ b/examples/gno.land/r/demo/atomicswap/atomicswap_test.gno @@ -145,6 +145,7 @@ func TestNewCustomGRC20Swap_Claim(t *testing.T) { - hashlock: 2bb80d537b1da3e38bd30361aa855686bde0eacd7162fef6a25fe97bf527a25b - timelock: 2009-02-14T00:31:30Z - remaining: 1h0m0s` + uassert.Equal(t, expected, swap.String()) uassert.Equal(t, expected, Render("1")) @@ -410,7 +411,7 @@ func TestRender(t *testing.T) { test20.PrivateLedger.Mint(alice, 100_000) testing.SetRealm(std.NewUserRealm(alice)) - userTeller := test20.Token.CallerTeller() + userTeller := test20.Token.RealmTeller() userTeller.Approve(rlm, 10_000) _, bobSwap := cross(NewCustomGRC20Swap)(bob, hashlockHex, timelock, test20.Token) diff --git a/examples/gno.land/r/demo/boards/z_10_a_filetest.gno b/examples/gno.land/r/demo/boards/z_10_a_filetest.gno index 2d0da5e5607..bf93c2fa78e 100644 --- a/examples/gno.land/r/demo/boards/z_10_a_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_10_a_filetest.gno @@ -32,4 +32,4 @@ func main() { } // Error: -// board not exist +// invalid non-user call diff --git a/examples/gno.land/r/demo/boards/z_10_b_filetest.gno b/examples/gno.land/r/demo/boards/z_10_b_filetest.gno index cdc45227346..736a35f19ee 100644 --- a/examples/gno.land/r/demo/boards/z_10_b_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_10_b_filetest.gno @@ -33,4 +33,4 @@ func main() { } // Error: -// thread not exist +// invalid non-user call diff --git a/examples/gno.land/r/demo/boards/z_10_c_filetest.gno b/examples/gno.land/r/demo/boards/z_10_c_filetest.gno index f7253ba4818..85f8988babb 100644 --- a/examples/gno.land/r/demo/boards/z_10_c_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_10_c_filetest.gno @@ -41,7 +41,7 @@ func main() { // \- [@gnouser123](/u/gnouser123), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1) \[[reply](/r/demo/boards$help&func=CreateReply&bid=1&postid=1&threadid=1)] \[[repost](/r/demo/boards$help&func=CreateRepost&bid=1&postid=1)] \[[x](/r/demo/boards$help&func=DeletePost&bid=1&postid=1&threadid=1)] // // > First reply of the First post -// > +// > // > \- [@gnouser123](/u/gnouser123), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1/2) \[[reply](/r/demo/boards$help&func=CreateReply&bid=1&postid=2&threadid=1)] \[[x](/r/demo/boards$help&func=DeletePost&bid=1&postid=2&threadid=1)] // // ---------------------------------------------------- diff --git a/examples/gno.land/r/demo/boards/z_11_a_filetest.gno b/examples/gno.land/r/demo/boards/z_11_a_filetest.gno index 030f8b87fb0..93646467a00 100644 --- a/examples/gno.land/r/demo/boards/z_11_a_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_11_a_filetest.gno @@ -33,4 +33,4 @@ func main() { } // Error: -// board not exist +// invalid non-user call diff --git a/examples/gno.land/r/demo/boards/z_11_b_filetest.gno b/examples/gno.land/r/demo/boards/z_11_b_filetest.gno index 088881372b5..cb890356f7a 100644 --- a/examples/gno.land/r/demo/boards/z_11_b_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_11_b_filetest.gno @@ -33,4 +33,4 @@ func main() { } // Error: -// thread not exist +// invalid non-user call diff --git a/examples/gno.land/r/demo/boards/z_11_c_filetest.gno b/examples/gno.land/r/demo/boards/z_11_c_filetest.gno index abd3335f370..47e47bcff49 100644 --- a/examples/gno.land/r/demo/boards/z_11_c_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_11_c_filetest.gno @@ -33,4 +33,4 @@ func main() { } // Error: -// post not exist +// invalid non-user call diff --git a/examples/gno.land/r/demo/boards/z_11_d_filetest.gno b/examples/gno.land/r/demo/boards/z_11_d_filetest.gno index c489c8bbc21..8b736270e32 100644 --- a/examples/gno.land/r/demo/boards/z_11_d_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_11_d_filetest.gno @@ -41,7 +41,7 @@ func main() { // \- [@gnouser123](/u/gnouser123), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1) \[[reply](/r/demo/boards$help&func=CreateReply&bid=1&postid=1&threadid=1)] \[[repost](/r/demo/boards$help&func=CreateRepost&bid=1&postid=1)] \[[x](/r/demo/boards$help&func=DeletePost&bid=1&postid=1&threadid=1)] // // > First reply of the First post -// > +// > // > \- [@gnouser123](/u/gnouser123), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1/2) \[[reply](/r/demo/boards$help&func=CreateReply&bid=1&postid=2&threadid=1)] \[[x](/r/demo/boards$help&func=DeletePost&bid=1&postid=2&threadid=1)] // // ---------------------------------------------------- @@ -51,6 +51,6 @@ func main() { // \- [@gnouser123](/u/gnouser123), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1) \[[reply](/r/demo/boards$help&func=CreateReply&bid=1&postid=1&threadid=1)] \[[repost](/r/demo/boards$help&func=CreateRepost&bid=1&postid=1)] \[[x](/r/demo/boards$help&func=DeletePost&bid=1&postid=1&threadid=1)] // // > Edited: First reply of the First post -// > +// > // > \- [@gnouser123](/u/gnouser123), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1/2) \[[reply](/r/demo/boards$help&func=CreateReply&bid=1&postid=2&threadid=1)] \[[x](/r/demo/boards$help&func=DeletePost&bid=1&postid=2&threadid=1)] // diff --git a/examples/gno.land/r/demo/boards/z_5_filetest.gno b/examples/gno.land/r/demo/boards/z_5_filetest.gno index 6eae728b28b..cb0a9c27800 100644 --- a/examples/gno.land/r/demo/boards/z_5_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_5_filetest.gno @@ -42,6 +42,6 @@ func main() { // > \- [@gnouser123](/u/gnouser123), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2/3) \[[reply](/r/demo/boards$help&func=CreateReply&bid=1&postid=3&threadid=2)] \[[x](/r/demo/boards$help&func=DeletePost&bid=1&postid=3&threadid=2)] // // > Second reply of the second post -// > +// > // > \- [@gnouser123](/u/gnouser123), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2/4) \[[reply](/r/demo/boards$help&func=CreateReply&bid=1&postid=4&threadid=2)] \[[x](/r/demo/boards$help&func=DeletePost&bid=1&postid=4&threadid=2)] // diff --git a/examples/gno.land/r/demo/boards/z_6_filetest.gno b/examples/gno.land/r/demo/boards/z_6_filetest.gno index 0b13ec5547e..eb0fc46999c 100644 --- a/examples/gno.land/r/demo/boards/z_6_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_6_filetest.gno @@ -42,12 +42,12 @@ func main() { // // > Reply of the second post // > \- [@gnouser123](/u/gnouser123), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2/3) \[[reply](/r/demo/boards$help&func=CreateReply&bid=1&postid=3&threadid=2)] \[[x](/r/demo/boards$help&func=DeletePost&bid=1&postid=3&threadid=2)] -// > +// > // > > First reply of the first reply -// > > +// > > // > > \- [@gnouser123](/u/gnouser123), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2/5) \[[reply](/r/demo/boards$help&func=CreateReply&bid=1&postid=5&threadid=2)] \[[x](/r/demo/boards$help&func=DeletePost&bid=1&postid=5&threadid=2)] // // > Second reply of the second post -// > +// > // > \- [@gnouser123](/u/gnouser123), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2/4) \[[reply](/r/demo/boards$help&func=CreateReply&bid=1&postid=4&threadid=2)] \[[x](/r/demo/boards$help&func=DeletePost&bid=1&postid=4&threadid=2)] // diff --git a/examples/gno.land/r/demo/boards/z_8_filetest.gno b/examples/gno.land/r/demo/boards/z_8_filetest.gno index 7dd2182af40..ccc2ad63609 100644 --- a/examples/gno.land/r/demo/boards/z_8_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_8_filetest.gno @@ -43,6 +43,6 @@ func main() { // _[see all 1 replies](/r/demo/boards:test_board/2/3)_ // // > First reply of the first reply -// > +// > // > \- [@gnouser123](/u/gnouser123), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2/5) \[[reply](/r/demo/boards$help&func=CreateReply&bid=1&postid=5&threadid=2)] \[[x](/r/demo/boards$help&func=DeletePost&bid=1&postid=5&threadid=2)] // diff --git a/examples/gno.land/r/demo/boards/z_9_a_filetest.gno b/examples/gno.land/r/demo/boards/z_9_a_filetest.gno index df0f3a2c500..277e85982bf 100644 --- a/examples/gno.land/r/demo/boards/z_9_a_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_9_a_filetest.gno @@ -24,4 +24,4 @@ func main() { } // Error: -// src board not exist +// invalid non-user call diff --git a/examples/gno.land/r/demo/boards/z_9_b_filetest.gno b/examples/gno.land/r/demo/boards/z_9_b_filetest.gno index d15d3772fde..0826409c741 100644 --- a/examples/gno.land/r/demo/boards/z_9_b_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_9_b_filetest.gno @@ -28,4 +28,4 @@ func main() { } // Error: -// dst board not exist +// invalid non-user call diff --git a/examples/gno.land/r/demo/disperse/disperse.gno b/examples/gno.land/r/demo/disperse/disperse.gno index 41319992905..5699f2a39b0 100644 --- a/examples/gno.land/r/demo/disperse/disperse.gno +++ b/examples/gno.land/r/demo/disperse/disperse.gno @@ -6,15 +6,12 @@ import ( tokens "gno.land/r/demo/grc20factory" ) -// Get address of Disperse realm -var realmAddr = std.CurrentRealm().Address() - // DisperseUgnot parses receivers and amounts and sends out ugnot // The function will send out the coins to the addresses and return the leftover coins to the caller // if there are any to return func DisperseUgnot(addresses []std.Address, coins std.Coins) { - coinSent := std.OriginSend() - caller := std.PreviousRealm().Address() + // CurrentRealm is the one that is calling this function since we didn't crossed + currentAddr := std.CurrentRealm().Address() banker := std.NewBanker(std.BankerTypeOriginSend) if len(addresses) != len(coins) { @@ -26,30 +23,44 @@ func DisperseUgnot(addresses []std.Address, coins std.Coins) { panic(ErrNegativeCoinAmount) } - if banker.GetCoins(realmAddr).AmountOf(coin.Denom) < coin.Amount { + if banker.GetCoins(currentAddr).AmountOf(coin.Denom) < coin.Amount { panic(ErrMismatchBetweenSentAndParams) } } // Send coins - for i, _ := range addresses { - banker.SendCoins(realmAddr, addresses[i], std.NewCoins(coins[i])) + for i := range addresses { + banker.SendCoins(currentAddr, addresses[i], std.NewCoins(coins[i])) } +} - // Return possible leftover coins - for _, coin := range coinSent { - leftoverAmt := banker.GetCoins(realmAddr).AmountOf(coin.Denom) - if leftoverAmt > 0 { - send := std.Coins{std.NewCoin(coin.Denom, leftoverAmt)} - banker.SendCoins(realmAddr, caller, send) - } +// DisperseUgnotString receives a string of addresses and a string of amounts +// and parses them to be used in DisperseUgnot +func DisperseUgnotString(addresses string, amounts string) { + parsedAddresses, err := parseAddresses(addresses) + if err != nil { + panic(err) } + + parsedAmounts, err := parseAmounts(amounts) + if err != nil { + panic(err) + } + + coins := make(std.Coins, len(parsedAmounts)) + for i, amount := range parsedAmounts { + coins[i] = std.NewCoin("ugnot", amount) + } + + DisperseUgnot(parsedAddresses, coins) } // DisperseGRC20 disperses tokens to multiple addresses // Note that it is necessary to approve the realm to spend the tokens before calling this function // see the corresponding filetests for examples func DisperseGRC20(addresses []std.Address, amounts []uint64, symbols []string) { + crossing() + caller := std.PreviousRealm().Address() if (len(addresses) != len(amounts)) || (len(amounts) != len(symbols)) { @@ -57,13 +68,15 @@ func DisperseGRC20(addresses []std.Address, amounts []uint64, symbols []string) } for i := 0; i < len(addresses); i++ { - tokens.TransferFrom(symbols[i], caller, addresses[i], amounts[i]) + cross(tokens.TransferFrom)(symbols[i], caller, addresses[i], amounts[i]) } } // DisperseGRC20String receives a string of addresses and a string of tokens // and parses them to be used in DisperseGRC20 func DisperseGRC20String(addresses string, tokens string) { + crossing() + parsedAddresses, err := parseAddresses(addresses) if err != nil { panic(err) @@ -76,24 +89,3 @@ func DisperseGRC20String(addresses string, tokens string) { DisperseGRC20(parsedAddresses, parsedAmounts, parsedSymbols) } - -// DisperseUgnotString receives a string of addresses and a string of amounts -// and parses them to be used in DisperseUgnot -func DisperseUgnotString(addresses string, amounts string) { - parsedAddresses, err := parseAddresses(addresses) - if err != nil { - panic(err) - } - - parsedAmounts, err := parseAmounts(amounts) - if err != nil { - panic(err) - } - - coins := make(std.Coins, len(parsedAmounts)) - for i, amount := range parsedAmounts { - coins[i] = std.NewCoin("ugnot", amount) - } - - DisperseUgnot(parsedAddresses, coins) -} diff --git a/examples/gno.land/r/demo/disperse/z_0_filetest.gno b/examples/gno.land/r/demo/disperse/z_0_filetest.gno index dd12b798163..be39cf1e2b4 100644 --- a/examples/gno.land/r/demo/disperse/z_0_filetest.gno +++ b/examples/gno.land/r/demo/disperse/z_0_filetest.gno @@ -6,30 +6,28 @@ package main import ( "std" - "testing" "gno.land/r/demo/disperse" ) func main() { - disperseAddr := std.DerivePkgAddr("gno.land/r/demo/disperse") - mainaddr := std.DerivePkgAddr("gno.land/r/demo/main") - - testing.SetOriginCaller(mainaddr) + mainAddr := std.DerivePkgAddr("gno.land/r/demo/main") + beneficiary1 := std.Address("g1dmt3sa5ucvecxuhf3j6ne5r0e3z4x7h6c03xc0") + beneficiary2 := std.Address("g1akeqsvhucjt8gf5yupyzjxsjd29wv8fayng37c") banker := std.NewBanker(std.BankerTypeRealmSend) + println("main balance before disperse:", banker.GetCoins(mainAddr)) - mainbal := banker.GetCoins(mainaddr) - println("main before:", mainbal) - - banker.SendCoins(mainaddr, disperseAddr, std.Coins{{"ugnot", 200}}) - testing.SetRealm(std.NewCodeRealm("gno.land/r/demo/disperse")) - disperse.DisperseUgnotString("g1dmt3sa5ucvecxuhf3j6ne5r0e3z4x7h6c03xc0,g1akeqsvhucjt8gf5yupyzjxsjd29wv8fayng37c", "150,50") + addressesStr := beneficiary1.String() + "," + beneficiary2.String() + disperse.DisperseUgnotString(addressesStr, "150,50") - mainbal = banker.GetCoins(mainaddr) - println("main after:", mainbal) + println("main balance after disperse:", banker.GetCoins(mainAddr)) + println("beneficiary1 balance:", banker.GetCoins(beneficiary1)) + println("beneficiary2 balance:", banker.GetCoins(beneficiary2)) } // Output: -// main before: 200ugnot -// main after: +// main balance before disperse: 200ugnot +// main balance after disperse: +// beneficiary1 balance: 150ugnot +// beneficiary2 balance: 50ugnot diff --git a/examples/gno.land/r/demo/disperse/z_1_filetest.gno b/examples/gno.land/r/demo/disperse/z_1_filetest.gno index 11f8bc7942d..af1784fdbd7 100644 --- a/examples/gno.land/r/demo/disperse/z_1_filetest.gno +++ b/examples/gno.land/r/demo/disperse/z_1_filetest.gno @@ -6,30 +6,28 @@ package main import ( "std" - "testing" "gno.land/r/demo/disperse" ) func main() { - disperseAddr := std.DerivePkgAddr("gno.land/r/demo/disperse") - mainaddr := std.DerivePkgAddr("gno.land/r/demo/main") - - testing.SetOriginCaller(mainaddr) + mainAddr := std.DerivePkgAddr("gno.land/r/demo/main") + beneficiary1 := std.Address("g1dmt3sa5ucvecxuhf3j6ne5r0e3z4x7h6c03xc0") + beneficiary2 := std.Address("g1akeqsvhucjt8gf5yupyzjxsjd29wv8fayng37c") banker := std.NewBanker(std.BankerTypeRealmSend) + println("main balance before disperse:", banker.GetCoins(mainAddr)) - mainbal := banker.GetCoins(mainaddr) - println("main before:", mainbal) - - banker.SendCoins(mainaddr, disperseAddr, std.Coins{{"ugnot", 300}}) - testing.SetRealm(std.NewCodeRealm("gno.land/r/demo/disperse")) - disperse.DisperseUgnotString("g1dmt3sa5ucvecxuhf3j6ne5r0e3z4x7h6c03xc0,g1akeqsvhucjt8gf5yupyzjxsjd29wv8fayng37c", "150,50") + addressesStr := beneficiary1.String() + "," + beneficiary2.String() + disperse.DisperseUgnotString(addressesStr, "150,50") - mainbal = banker.GetCoins(mainaddr) - println("main after:", mainbal) + println("main balance after disperse:", banker.GetCoins(mainAddr)) + println("beneficiary1 balance:", banker.GetCoins(beneficiary1)) + println("beneficiary2 balance:", banker.GetCoins(beneficiary2)) } // Output: -// main before: 300ugnot -// main after: 100ugnot +// main balance before disperse: 300ugnot +// main balance after disperse: 100ugnot +// beneficiary1 balance: 150ugnot +// beneficiary2 balance: 50ugnot diff --git a/examples/gno.land/r/demo/disperse/z_2_filetest.gno b/examples/gno.land/r/demo/disperse/z_2_filetest.gno index 8cb580e2968..0304fe69876 100644 --- a/examples/gno.land/r/demo/disperse/z_2_filetest.gno +++ b/examples/gno.land/r/demo/disperse/z_2_filetest.gno @@ -1,27 +1,25 @@ // PKGPATH: gno.land/r/demo/main -// SEND: 300ugnot +// SEND: 100ugnot package main import ( "std" - "testing" "gno.land/r/demo/disperse" ) func main() { - disperseAddr := std.DerivePkgAddr("gno.land/r/demo/disperse") - mainaddr := std.DerivePkgAddr("gno.land/r/demo/main") - - testing.SetOriginCaller(mainaddr) + mainAddr := std.DerivePkgAddr("gno.land/r/demo/main") + beneficiary1 := std.Address("g1dmt3sa5ucvecxuhf3j6ne5r0e3z4x7h6c03xc0") + beneficiary2 := std.Address("g1akeqsvhucjt8gf5yupyzjxsjd29wv8fayng37c") banker := std.NewBanker(std.BankerTypeRealmSend) + println("main balance before disperse:", banker.GetCoins(mainAddr)) - banker.SendCoins(mainaddr, disperseAddr, std.Coins{{"ugnot", 100}}) - testing.SetRealm(std.NewCodeRealm("gno.land/r/demo/disperse")) - disperse.DisperseUgnotString("g1dmt3sa5ucvecxuhf3j6ne5r0e3z4x7h6c03xc0,g1akeqsvhucjt8gf5yupyzjxsjd29wv8fayng37c", "150,50") + addressesStr := beneficiary1.String() + "," + beneficiary2.String() + disperse.DisperseUgnotString(addressesStr, "150,50") } // Error: diff --git a/examples/gno.land/r/demo/disperse/z_3_filetest.gno b/examples/gno.land/r/demo/disperse/z_3_filetest.gno index 8ac0bd000c3..299f3ee6c8b 100644 --- a/examples/gno.land/r/demo/disperse/z_3_filetest.gno +++ b/examples/gno.land/r/demo/disperse/z_3_filetest.gno @@ -14,34 +14,32 @@ import ( func main() { disperseAddr := std.DerivePkgAddr("gno.land/r/demo/disperse") - mainaddr := std.DerivePkgAddr("gno.land/r/demo/main") + mainAddr := std.DerivePkgAddr("gno.land/r/demo/main") beneficiary1 := std.Address("g1dmt3sa5ucvecxuhf3j6ne5r0e3z4x7h6c03xc0") beneficiary2 := std.Address("g1akeqsvhucjt8gf5yupyzjxsjd29wv8fayng37c") - testing.SetOriginCaller(mainaddr) + testing.SetOriginCaller(mainAddr) - banker := std.NewBanker(std.BankerTypeRealmSend) + cross(tokens.New)("test", "TEST", 4, 0, 0) + cross(tokens.Mint)("TEST", mainAddr, 200) + println("main balance before:", tokens.BalanceOf("TEST", mainAddr)) - tokens.New("test", "TEST", 4, 0, 0) - tokens.Mint("TEST", mainaddr, 200) + cross(tokens.Approve)("TEST", disperseAddr, 200) + println("disperse allowance before:", tokens.Allowance("TEST", mainAddr, disperseAddr)) - mainbal := tokens.BalanceOf("TEST", mainaddr) - println("main before:", mainbal) + addressesStr := beneficiary1.String() + "," + beneficiary2.String() + cross(disperse.DisperseGRC20String)(addressesStr, "150TEST,50TEST") - tokens.Approve("TEST", disperseAddr, 200) - - disperse.DisperseGRC20String("g1dmt3sa5ucvecxuhf3j6ne5r0e3z4x7h6c03xc0,g1akeqsvhucjt8gf5yupyzjxsjd29wv8fayng37c", "150TEST,50TEST") - - mainbal = tokens.BalanceOf("TEST", mainaddr) - println("main after:", mainbal) - ben1bal := tokens.BalanceOf("TEST", beneficiary1) - println("beneficiary1:", ben1bal) - ben2bal := tokens.BalanceOf("TEST", beneficiary2) - println("beneficiary2:", ben2bal) + println("main balance after:", tokens.BalanceOf("TEST", mainAddr)) + println("disperse allowance after:", tokens.Allowance("TEST", mainAddr, disperseAddr)) + println("beneficiary1 balance:", tokens.BalanceOf("TEST", beneficiary1)) + println("beneficiary2 balance:", tokens.BalanceOf("TEST", beneficiary2)) } // Output: -// main before: 200 -// main after: 0 -// beneficiary1: 150 -// beneficiary2: 50 +// main balance before: 200 +// disperse allowance before: 200 +// main balance after: 0 +// disperse allowance after: 0 +// beneficiary1 balance: 150 +// beneficiary2 balance: 50 diff --git a/examples/gno.land/r/demo/disperse/z_4_filetest.gno b/examples/gno.land/r/demo/disperse/z_4_filetest.gno index a1e629b33f0..257b9ce3e7a 100644 --- a/examples/gno.land/r/demo/disperse/z_4_filetest.gno +++ b/examples/gno.land/r/demo/disperse/z_4_filetest.gno @@ -14,37 +14,49 @@ import ( func main() { disperseAddr := std.DerivePkgAddr("gno.land/r/demo/disperse") - mainaddr := std.DerivePkgAddr("gno.land/r/demo/main") + mainAddr := std.DerivePkgAddr("gno.land/r/demo/main") beneficiary1 := std.Address("g1dmt3sa5ucvecxuhf3j6ne5r0e3z4x7h6c03xc0") beneficiary2 := std.Address("g1akeqsvhucjt8gf5yupyzjxsjd29wv8fayng37c") - testing.SetOriginCaller(mainaddr) + testing.SetOriginCaller(mainAddr) - banker := std.NewBanker(std.BankerTypeRealmSend) + cross(tokens.New)("test1", "TEST1", 4, 0, 0) + cross(tokens.Mint)("TEST1", mainAddr, 200) + println("main balance before (TEST1):", tokens.BalanceOf("TEST1", mainAddr)) - tokens.New("test1", "TEST1", 4, 0, 0) - tokens.Mint("TEST1", mainaddr, 200) - tokens.New("test2", "TEST2", 4, 0, 0) - tokens.Mint("TEST2", mainaddr, 200) + cross(tokens.New)("test2", "TEST2", 4, 0, 0) + cross(tokens.Mint)("TEST2", mainAddr, 200) + println("main balance before (TEST2):", tokens.BalanceOf("TEST2", mainAddr)) - mainbal := tokens.BalanceOf("TEST1", mainaddr) + tokens.BalanceOf("TEST2", mainaddr) - println("main before:", mainbal) + cross(tokens.Approve)("TEST1", disperseAddr, 200) + println("disperse allowance before (TEST1):", tokens.Allowance("TEST1", mainAddr, disperseAddr)) - tokens.Approve("TEST1", disperseAddr, 200) - tokens.Approve("TEST2", disperseAddr, 200) + cross(tokens.Approve)("TEST2", disperseAddr, 200) + println("disperse allowance before (TEST2):", tokens.Allowance("TEST2", mainAddr, disperseAddr)) - disperse.DisperseGRC20String("g1dmt3sa5ucvecxuhf3j6ne5r0e3z4x7h6c03xc0,g1akeqsvhucjt8gf5yupyzjxsjd29wv8fayng37c", "200TEST1,200TEST2") + addressesStr := beneficiary1.String() + "," + beneficiary2.String() + cross(disperse.DisperseGRC20String)(addressesStr, "200TEST1,200TEST2") - mainbal = tokens.BalanceOf("TEST1", mainaddr) + tokens.BalanceOf("TEST2", mainaddr) - println("main after:", mainbal) - ben1bal := tokens.BalanceOf("TEST1", beneficiary1) + tokens.BalanceOf("TEST2", beneficiary1) - println("beneficiary1:", ben1bal) - ben2bal := tokens.BalanceOf("TEST1", beneficiary2) + tokens.BalanceOf("TEST2", beneficiary2) - println("beneficiary2:", ben2bal) + println("main balance after (TEST1):", tokens.BalanceOf("TEST1", mainAddr)) + println("main balance after (TEST2):", tokens.BalanceOf("TEST2", mainAddr)) + println("disperse allowance after (TEST1):", tokens.Allowance("TEST1", mainAddr, disperseAddr)) + println("disperse allowance after (TEST2):", tokens.Allowance("TEST2", mainAddr, disperseAddr)) + println("beneficiary1 balance (TEST1):", tokens.BalanceOf("TEST1", beneficiary1)) + println("beneficiary1 balance (TEST2):", tokens.BalanceOf("TEST2", beneficiary1)) + println("beneficiary2 balance (TEST1):", tokens.BalanceOf("TEST1", beneficiary2)) + println("beneficiary2 balance (TEST2):", tokens.BalanceOf("TEST2", beneficiary2)) } // Output: -// main before: 400 -// main after: 0 -// beneficiary1: 200 -// beneficiary2: 200 +// main balance before (TEST1): 200 +// main balance before (TEST2): 200 +// disperse allowance before (TEST1): 200 +// disperse allowance before (TEST2): 200 +// main balance after (TEST1): 0 +// main balance after (TEST2): 0 +// disperse allowance after (TEST1): 0 +// disperse allowance after (TEST2): 0 +// beneficiary1 balance (TEST1): 200 +// beneficiary1 balance (TEST2): 0 +// beneficiary2 balance (TEST1): 0 +// beneficiary2 balance (TEST2): 200 diff --git a/examples/gno.land/r/demo/foo20/foo20.gno b/examples/gno.land/r/demo/foo20/foo20.gno index d6912127748..d68e50c10d8 100644 --- a/examples/gno.land/r/demo/foo20/foo20.gno +++ b/examples/gno.land/r/demo/foo20/foo20.gno @@ -95,6 +95,6 @@ func Render(path string) string { func checkErr(err error) { if err != nil { - panic(err) + panic(err.Error()) } } diff --git a/examples/gno.land/r/demo/foo20/foo20_test.gno b/examples/gno.land/r/demo/foo20/foo20_test.gno index 5dc6504be72..a2c4432ce92 100644 --- a/examples/gno.land/r/demo/foo20/foo20_test.gno +++ b/examples/gno.land/r/demo/foo20/foo20_test.gno @@ -63,15 +63,16 @@ func TestErrConditions(t *testing.T) { ) type test struct { - name string - msg string - fn func() + name string + msg string + isCross bool + fn func() } privateLedger.Mint(std.Address(admin), 10000) { tests := []test{ - {"Transfer(admin, 1)", "cannot send transfer to self", func() { + {"Transfer(admin, 1)", "cannot send transfer to self", false, func() { // XXX: should replace with: Transfer(admin, 1) // but there is currently a limitation in manipulating the frame stack and simulate // calling this package from an outside point of view. @@ -80,12 +81,36 @@ func TestErrConditions(t *testing.T) { panic(err) } }}, - {"Approve(empty, 1))", "invalid address", func() { cross(Approve)(empty, 1) }}, + {"Approve(empty, 1))", "invalid address", true, func() { cross(Approve)(empty, 1) }}, } for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - uassert.PanicsWithMessage(t, tc.msg, tc.fn) + tuassert.PanicsWithMessageting.T) { + if tc.isCross { + uassert.AbortsWithMessage(t, tc.msg, tc.fn) + } else { + uassert.PanicsWithMessage(t, tc.msg, tc.fn) + } }) } } } + +//func TestNewFoo20(t *testing.T) { +// t.Run("invalid input", func(t *testing.T) { +// testCases := []struct { +// msg string +// fn func() +// }{ +// // Test AbortsWithMessage +// uassert.PanicsWithMessage", func() { NewFoo20("foo", "f", 0) }}, +// {"symbol cannot be empty", func() { NewFoo20("foo", "", 1) }}, +// {"name cannot be empty", func() { NewFoo20("", "f", 1) }}, +// } +// for _, tc := range testCases { +// uassert.AbortsWithMessage(t, tc.msg, tc.fn) +// } +// }) +// t.Run("transfer", func(t *testing.T) { +// // ... existing code ... +// }) +//} diff --git a/examples/gno.land/r/demo/grc20factory/grc20factory.gno b/examples/gno.land/r/demo/grc20factory/grc20factory.gno index d15074e9350..1df83a1699b 100644 --- a/examples/gno.land/r/demo/grc20factory/grc20factory.gno +++ b/examples/gno.land/r/demo/grc20factory/grc20factory.gno @@ -21,7 +21,9 @@ type instance struct { } func New(name, symbol string, decimals uint, initialMint, faucet uint64) { - caller := std.OriginCaller() + crossing() + + caller := std.PreviousRealm().Address() NewWithAdmin(name, symbol, decimals, initialMint, faucet, caller) } diff --git a/examples/gno.land/r/demo/groups/public.gno b/examples/gno.land/r/demo/groups/public.gno index c4ea7432ec2..d5500439433 100644 --- a/examples/gno.land/r/demo/groups/public.gno +++ b/examples/gno.land/r/demo/groups/public.gno @@ -18,6 +18,7 @@ func GetGroupIDFromName(name string) (GroupID, bool) { } func CreateGroup(name string) GroupID { + crossing() std.AssertOriginCall() caller := std.OriginCaller() usernameOf(caller) @@ -30,6 +31,7 @@ func CreateGroup(name string) GroupID { } func AddMember(gid GroupID, address string, weight int, metadata string) MemberID { + crossing() std.AssertOriginCall() caller := std.OriginCaller() usernameOf(caller) @@ -51,6 +53,7 @@ func AddMember(gid GroupID, address string, weight int, metadata string) MemberI } func DeleteGroup(gid GroupID) { + crossing() std.AssertOriginCall() caller := std.OriginCaller() group := getGroup(gid) @@ -61,6 +64,7 @@ func DeleteGroup(gid GroupID) { } func DeleteMember(gid GroupID, mid MemberID) { + crossing() std.AssertOriginCall() caller := std.OriginCaller() group := getGroup(gid) diff --git a/examples/gno.land/r/demo/groups/z_0_a_filetest.gno b/examples/gno.land/r/demo/groups/z_0_a_filetest.gno index b31613f5554..14dddacc42c 100644 --- a/examples/gno.land/r/demo/groups/z_0_a_filetest.gno +++ b/examples/gno.land/r/demo/groups/z_0_a_filetest.gno @@ -10,7 +10,7 @@ import ( var gid groups.GroupID func main() { - gid = groups.CreateGroup("test_group") + gid = cross(groups.CreateGroup)("test_group") println(gid) println(groups.Render("")) } diff --git a/examples/gno.land/r/demo/groups/z_0_c_filetest.gno b/examples/gno.land/r/demo/groups/z_0_c_filetest.gno index d9500e6ebbc..46c16119034 100644 --- a/examples/gno.land/r/demo/groups/z_0_c_filetest.gno +++ b/examples/gno.land/r/demo/groups/z_0_c_filetest.gno @@ -15,8 +15,8 @@ var gid groups.GroupID func main() { testing.SetRealm(std.NewUserRealm(std.Address("g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm"))) // so that CurrentRealm.Addr() matches OrigCaller - users.Register("gnouser123") - gid = groups.CreateGroup("test_group") + cross(users.Register)("gnouser123") + gid = cross(groups.CreateGroup)("test_group") println(gid) println(groups.Render("")) } diff --git a/examples/gno.land/r/demo/groups/z_1_a_filetest.gno b/examples/gno.land/r/demo/groups/z_1_a_filetest.gno index 906e2cdfa9f..8b276469376 100644 --- a/examples/gno.land/r/demo/groups/z_1_a_filetest.gno +++ b/examples/gno.land/r/demo/groups/z_1_a_filetest.gno @@ -19,30 +19,30 @@ const admin = std.Address("g1manfred47kzduec920z88wfr64ylksmdcedlf5") func main() { caller := std.OriginCaller() // main testing.SetRealm(std.NewUserRealm(caller)) - users.Register("main123") + cross(users.Register)("main123") test1 := testutils.TestAddress("gnouser1") testing.SetOriginCaller(test1) testing.SetRealm(std.NewUserRealm(test1)) - users.Register("test123") + cross(users.Register)("test123") test2 := testutils.TestAddress("gnouser2") testing.SetOriginCaller(test2) testing.SetRealm(std.NewUserRealm(test2)) - users.Register("test223") + cross(users.Register)("test223") test3 := testutils.TestAddress("gnouser3") testing.SetOriginCaller(test3) testing.SetRealm(std.NewUserRealm(test3)) - users.Register("test323") + cross(users.Register)("test323") testing.SetOriginCaller(caller) testing.SetRealm(std.NewUserRealm(caller)) - gid = groups.CreateGroup("test_group") + gid = cross(groups.CreateGroup)("test_group") println(gid) - groups.AddMember(gid, test3.String(), 32, "i am from UAE") + cross(groups.AddMember)(gid, test3.String(), 32, "i am from UAE") println(groups.Render("test_group")) } diff --git a/examples/gno.land/r/demo/groups/z_1_b_filetest.gno b/examples/gno.land/r/demo/groups/z_1_b_filetest.gno index 7bb4f65281a..de11a18a1f9 100644 --- a/examples/gno.land/r/demo/groups/z_1_b_filetest.gno +++ b/examples/gno.land/r/demo/groups/z_1_b_filetest.gno @@ -16,11 +16,11 @@ var gid groups.GroupID func main() { caller := std.OriginCaller() testing.SetRealm(std.NewUserRealm(caller)) - users.Register("gnouser123") + cross(users.Register)("gnouser123") - gid = groups.CreateGroup("test_group") + gid = cross(groups.CreateGroup)("test_group") println(gid) - groups.AddMember(2, "g1vahx7atnv4erxh6lta047h6lta047h6ll85gpy", 55, "metadata3") + cross(groups.AddMember)(2, "g1vahx7atnv4erxh6lta047h6lta047h6ll85gpy", 55, "metadata3") println(groups.Render("")) } diff --git a/examples/gno.land/r/demo/groups/z_1_c_filetest.gno b/examples/gno.land/r/demo/groups/z_1_c_filetest.gno index 9c34c6c108c..0285bec901a 100644 --- a/examples/gno.land/r/demo/groups/z_1_c_filetest.gno +++ b/examples/gno.land/r/demo/groups/z_1_c_filetest.gno @@ -14,7 +14,7 @@ import ( var gid groups.GroupID func main() { - gid = groups.CreateGroup("test_group") + gid = cross(groups.CreateGroup)("test_group") println(gid) // add member via anon user @@ -22,7 +22,7 @@ func main() { testing.SetOriginCaller(test2) testing.SetOriginSend(std.Coins{{"ugnot", 9000000}}) - groups.AddMember(gid, test2.String(), 42, "metadata3") + cross(groups.AddMember)(gid, test2.String(), 42, "metadata3") } // Error: diff --git a/examples/gno.land/r/demo/groups/z_2_a_filetest.gno b/examples/gno.land/r/demo/groups/z_2_a_filetest.gno index 28b4a5f8ed4..5caaf1261ae 100644 --- a/examples/gno.land/r/demo/groups/z_2_a_filetest.gno +++ b/examples/gno.land/r/demo/groups/z_2_a_filetest.gno @@ -17,34 +17,34 @@ var gid groups.GroupID const admin = std.Address("g1manfred47kzduec920z88wfr64ylksmdcedlf5") func main() { - caller := std.OriginCaller() // main + caller := std.OriginCaller() // main123 testing.SetRealm(std.NewUserRealm(caller)) - users.Register("main123") + cross(users.Register)("main123") test1 := testutils.TestAddress("gnouser1") testing.SetOriginCaller(test1) testing.SetRealm(std.NewUserRealm(test1)) - users.Register("test123") + cross(users.Register)("test123") test2 := testutils.TestAddress("gnouser2") testing.SetOriginCaller(test2) testing.SetRealm(std.NewUserRealm(test2)) - users.Register("test223") + cross(users.Register)("test223") test3 := testutils.TestAddress("gnouser3") testing.SetOriginCaller(test3) testing.SetRealm(std.NewUserRealm(test3)) - users.Register("test323") + cross(users.Register)("test323") testing.SetOriginCaller(caller) testing.SetRealm(std.NewUserRealm(caller)) - gid = groups.CreateGroup("test_group") + gid = cross(groups.CreateGroup)("test_group") println(groups.Render("test_group")) - groups.AddMember(gid, test2.String(), 42, "metadata3") + cross(groups.AddMember)(gid, test2.String(), 42, "metadata3") - groups.DeleteMember(gid, 0) + cross(groups.DeleteMember)(gid, 0) println(groups.RenderGroup(gid)) } 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 f799c69bde7..47550672386 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 @@ -11,10 +11,10 @@ import ( var gid groups.GroupID func main() { - users.Register("gnouser123") - gid = groups.CreateGroup("test_group") + cross(users.Register)("gnouser123") + gid = cross(groups.CreateGroup)("test_group") println(gid) - groups.DeleteMember(2, 0) + cross(groups.DeleteMember)(2, 0) println(groups.Render("")) } 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 f894706bc0f..54059f453e9 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 @@ -15,8 +15,8 @@ import ( var gid groups.GroupID func main() { - users.Register("gnouser123") - gid = groups.CreateGroup("test_group") + cross(users.Register)("gnouser123") + gid = cross(groups.CreateGroup)("test_group") println(gid) // delete member via anon user @@ -24,7 +24,7 @@ func main() { testing.SetOriginCaller(test2) testing.SetOriginSend(std.Coins{{"ugnot", 9000000}}) - groups.DeleteMember(gid, 0) + cross(groups.DeleteMember)(gid, 0) println(groups.Render("")) } diff --git a/examples/gno.land/r/demo/groups/z_2_e_filetest.gno b/examples/gno.land/r/demo/groups/z_2_e_filetest.gno index 866e4d2bc74..9c8f59342f7 100644 --- a/examples/gno.land/r/demo/groups/z_2_e_filetest.gno +++ b/examples/gno.land/r/demo/groups/z_2_e_filetest.gno @@ -15,10 +15,10 @@ var gid groups.GroupID func main() { testing.SetRealm(std.NewUserRealm(std.Address("g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm"))) // so that CurrentRealm.Addr() matches OrigCaller - users.Register("gnouser123") - gid = groups.CreateGroup("test_group") + cross(users.Register)("gnouser123") + gid = cross(groups.CreateGroup)("test_group") println(gid) - groups.DeleteGroup(gid) + cross(groups.DeleteGroup)(gid) println(groups.Render("")) } diff --git a/examples/gno.land/r/demo/groups/z_2_f_filetest.gno b/examples/gno.land/r/demo/groups/z_2_f_filetest.gno index 7d8a9d14537..89da3931d77 100644 --- a/examples/gno.land/r/demo/groups/z_2_f_filetest.gno +++ b/examples/gno.land/r/demo/groups/z_2_f_filetest.gno @@ -16,12 +16,12 @@ var gid groups.GroupID func main() { caller := std.OriginCaller() testing.SetRealm(std.NewUserRealm(caller)) - users.Register("gnouser123") + cross(users.Register)("gnouser123") - gid = groups.CreateGroup("test_group") + gid = cross(groups.CreateGroup)("test_group") println(gid) - groups.DeleteGroup(20) + cross(groups.DeleteGroup)(20) println(groups.Render("")) } diff --git a/examples/gno.land/r/demo/groups/z_2_g_filetest.gno b/examples/gno.land/r/demo/groups/z_2_g_filetest.gno index 80987940b38..d00eb6a9e1d 100644 --- a/examples/gno.land/r/demo/groups/z_2_g_filetest.gno +++ b/examples/gno.land/r/demo/groups/z_2_g_filetest.gno @@ -17,9 +17,9 @@ var gid groups.GroupID func main() { caller := std.OriginCaller() testing.SetRealm(std.NewUserRealm(caller)) - users.Register("gnouser123") + cross(users.Register)("gnouser123") - gid = groups.CreateGroup("test_group") + gid = cross(groups.CreateGroup)("test_group") println(gid) // delete group via anon user @@ -27,7 +27,7 @@ func main() { testing.SetOriginCaller(test2) testing.SetOriginSend(std.Coins{{"ugnot", 9000000}}) - groups.DeleteGroup(gid) + cross(groups.DeleteGroup)(gid) println(groups.Render("")) } diff --git a/examples/gno.land/r/demo/nft/z_0_filetest.gno b/examples/gno.land/r/demo/nft/z_0_filetest.gno deleted file mode 100644 index bf624542d24..00000000000 --- a/examples/gno.land/r/demo/nft/z_0_filetest.gno +++ /dev/null @@ -1,232 +0,0 @@ -// PKGPATH: gno.land/r/demo/nft_test -package nft_test - -import ( - "gno.land/p/demo/testutils" - "gno.land/r/demo/nft" -) - -func main() { - addr1 := testutils.TestAddress("addr1") - // addr2 := testutils.TestAddress("addr2") - grc721 := nft.GetToken() - tid := grc721.Mint(addr1, "NFT#1") - println(grc721.OwnerOf(tid)) - println(addr1) -} - -// Output: -// g1v9jxgu33ta047h6lta047h6lta047h6l43dqc5 -// g1v9jxgu33ta047h6lta047h6lta047h6l43dqc5 - -// Realm: -// finalizerealm["gno.land/r/demo/nft"] -// finalizerealm["gno.land/r/demo/nft"] -// c[67c479d3d51d4056b2f4111d5352912a00be311e:11]={ -// "Fields": [ -// { -// "T": { -// "@type": "/gno.RefType", -// "ID": "std.Address" -// }, -// "V": { -// "@type": "/gno.StringValue", -// "value": "g1v9jxgu33ta047h6lta047h6lta047h6l43dqc5" -// } -// }, -// { -// "T": { -// "@type": "/gno.RefType", -// "ID": "std.Address" -// } -// }, -// { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/grc/grc721.TokenID" -// }, -// "V": { -// "@type": "/gno.StringValue", -// "value": "1" -// } -// }, -// { -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "16" -// }, -// "V": { -// "@type": "/gno.StringValue", -// "value": "NFT#1" -// } -// } -// ], -// "ObjectInfo": { -// "ID": "67c479d3d51d4056b2f4111d5352912a00be311e:11", -// "ModTime": "0", -// "OwnerID": "67c479d3d51d4056b2f4111d5352912a00be311e:10", -// "RefCount": "1" -// } -// } -// c[67c479d3d51d4056b2f4111d5352912a00be311e:10]={ -// "ObjectInfo": { -// "ID": "67c479d3d51d4056b2f4111d5352912a00be311e:10", -// "ModTime": "0", -// "OwnerID": "67c479d3d51d4056b2f4111d5352912a00be311e:9", -// "RefCount": "1" -// }, -// "Value": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/nft.NFToken" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "564a9e78be869bd258fc3c9ad56f5a75ed68818f", -// "ObjectID": "67c479d3d51d4056b2f4111d5352912a00be311e:11" -// } -// } -// } -// c[67c479d3d51d4056b2f4111d5352912a00be311e:9]={ -// "Fields": [ -// { -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "16" -// }, -// "V": { -// "@type": "/gno.StringValue", -// "value": "1" -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/nft.NFToken" -// } -// }, -// "V": { -// "@type": "/gno.PointerValue", -// "Base": { -// "@type": "/gno.RefValue", -// "Hash": "b53ffc464e1b5655d19b9d5277f3491717c24aca", -// "ObjectID": "67c479d3d51d4056b2f4111d5352912a00be311e:10" -// }, -// "Index": "0", -// "TV": null -// } -// }, -// { -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "64" -// } -// }, -// { -// "N": "AQAAAAAAAAA=", -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "32" -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// } -// } -// } -// ], -// "ObjectInfo": { -// "ID": "67c479d3d51d4056b2f4111d5352912a00be311e:9", -// "ModTime": "0", -// "OwnerID": "67c479d3d51d4056b2f4111d5352912a00be311e:8", -// "RefCount": "1" -// } -// } -// c[67c479d3d51d4056b2f4111d5352912a00be311e:8]={ -// "ObjectInfo": { -// "ID": "67c479d3d51d4056b2f4111d5352912a00be311e:8", -// "ModTime": "0", -// "OwnerID": "67c479d3d51d4056b2f4111d5352912a00be311e:5", -// "RefCount": "1" -// }, -// "Value": { -// "T": { -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// }, -// "V": { -// "@type": "/gno.RefValue", -// "Hash": "b1d928b3716b147c92730e8d234162bec2f0f2fc", -// "ObjectID": "67c479d3d51d4056b2f4111d5352912a00be311e:9" -// } -// } -// } -// u[67c479d3d51d4056b2f4111d5352912a00be311e:5]= -// @@ -7,12 +7,22 @@ -// "@type": "/gno.RefType", -// "ID": "gno.land/p/demo/avl.Node" -// } -// + }, -// + "V": { -// + "@type": "/gno.PointerValue", -// + "Base": { -// + "@type": "/gno.RefValue", -// + "Hash": "b229b824842ec3e7f2341e33d0fa0ca77af2f480", -// + "ObjectID": "67c479d3d51d4056b2f4111d5352912a00be311e:8" -// + }, -// + "Index": "0", -// + "TV": null -// } -// } -// ], -// "ObjectInfo": { -// "ID": "67c479d3d51d4056b2f4111d5352912a00be311e:5", -// - "ModTime": "0", -// + "ModTime": "7", -// "OwnerID": "67c479d3d51d4056b2f4111d5352912a00be311e:4", -// "RefCount": "1" -// } -// u[67c479d3d51d4056b2f4111d5352912a00be311e:4]= -// @@ -2,6 +2,7 @@ -// "Fields": [ -// {}, -// { -// + "N": "AQAAAAAAAAA=", -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "32" -// @@ -14,7 +15,7 @@ -// }, -// "V": { -// "@type": "/gno.RefValue", -// - "Hash": "872ac4fb1c12756da1e64712e7496f1d8db57ee0", -// + "Hash": "1e0b9dddb406b4f50500a022266a4cb8a4ea38c6", -// "ObjectID": "67c479d3d51d4056b2f4111d5352912a00be311e:5" -// } -// }, -// @@ -32,7 +33,7 @@ -// ], -// "ObjectInfo": { -// "ID": "67c479d3d51d4056b2f4111d5352912a00be311e:4", -// - "ModTime": "0", -// + "ModTime": "7", -// "OwnerID": "67c479d3d51d4056b2f4111d5352912a00be311e:3", -// "RefCount": "1" -// } -// finalizerealm["gno.land/r/demo/nft"] -// finalizerealm["gno.land/r/demo/nft_test"] diff --git a/examples/gno.land/r/demo/tamagotchi/realm.gno b/examples/gno.land/r/demo/tamagotchi/realm.gno index 4222a9c63b5..471729cf3a8 100644 --- a/examples/gno.land/r/demo/tamagotchi/realm.gno +++ b/examples/gno.land/r/demo/tamagotchi/realm.gno @@ -14,6 +14,8 @@ func init() { } func Reset(optionalName string) string { + crossing() + name := optionalName if name == "" { height := std.ChainHeight() @@ -26,16 +28,22 @@ func Reset(optionalName string) string { } func Feed() string { + crossing() + t.Feed() return t.Markdown() } func Play() string { + crossing() + t.Play() return t.Markdown() } func Heal() string { + crossing() + t.Heal() return t.Markdown() } diff --git a/examples/gno.land/r/demo/tamagotchi/z0_filetest.gno b/examples/gno.land/r/demo/tamagotchi/z0_filetest.gno index 4072c0b30d7..d3dd5bcff7b 100644 --- a/examples/gno.land/r/demo/tamagotchi/z0_filetest.gno +++ b/examples/gno.land/r/demo/tamagotchi/z0_filetest.gno @@ -5,7 +5,7 @@ import ( ) func main() { - tamagotchi.Reset("tamagnotchi") + cross(tamagotchi.Reset)("tamagnotchi") println(tamagotchi.Render("")) } diff --git a/examples/gno.land/r/demo/tests/crossrealm_c/crossrealm.gno b/examples/gno.land/r/demo/tests/crossrealm_c/crossrealm.gno index 5bc20cd189e..57daa000250 100644 --- a/examples/gno.land/r/demo/tests/crossrealm_c/crossrealm.gno +++ b/examples/gno.land/r/demo/tests/crossrealm_c/crossrealm.gno @@ -1,9 +1,10 @@ package crossrealm_c import ( + "std" + "gno.land/p/demo/tests/p_crossrealm" "gno.land/r/demo/tests/crossrealm" - "std" ) func EntryPoint() { diff --git a/examples/gno.land/r/demo/tests/tests.gno b/examples/gno.land/r/demo/tests/tests.gno index 08b64538cf2..6a947d03a01 100644 --- a/examples/gno.land/r/demo/tests/tests.gno +++ b/examples/gno.land/r/demo/tests/tests.gno @@ -50,13 +50,13 @@ func CallIsOriginCall() bool { func CallSubtestsAssertOriginCall() { crossing() - rsubtests.CallAssertOriginCall() + cross(rsubtests.CallAssertOriginCall)() } func CallSubtestsIsOriginCall() bool { crossing() - return rsubtests.CallIsOriginCall() + return cross(rsubtests.CallIsOriginCall)() } //---------------------------------------- @@ -139,17 +139,17 @@ func ExecSwitch(fn func()) { func IsCallerSubPath() bool { crossing() - return nestedpkg.IsCallerSubPath() + return cross(nestedpkg.IsCallerSubPath)() } func IsCallerParentPath() bool { crossing() - return nestedpkg.IsCallerParentPath() + return cross(nestedpkg.IsCallerParentPath)() } func HasCallerSameNamespace() bool { crossing() - return nestedpkg.IsSameNamespace() + return cross(nestedpkg.IsSameNamespace)() } diff --git a/examples/gno.land/r/demo/tests/tests_test.gno b/examples/gno.land/r/demo/tests/tests_test.gno index 24b4da770cf..eadd284658b 100644 --- a/examples/gno.land/r/demo/tests/tests_test.gno +++ b/examples/gno.land/r/demo/tests/tests_test.gno @@ -12,8 +12,8 @@ func TestAssertOriginCall(t *testing.T) { // CallAssertOriginCall(): no panic caller := testutils.TestAddress("caller") testing.SetRealm(std.NewUserRealm(caller)) - tests.CallAssertOriginCall() - if !tests.CallIsOriginCall() { + cross(tests.CallAssertOriginCall)() + if !cross(tests.CallIsOriginCall)() { t.Errorf("expected IsOriginCall=true but got false") } @@ -29,10 +29,10 @@ func TestAssertOriginCall(t *testing.T) { }() // if called inside a function literal, this is no longer an origin call // because there's one additional frame (the function literal block). - if tests.CallIsOriginCall() { + if cross(tests.CallIsOriginCall)() { t.Errorf("expected IsOriginCall=false but got true") } - tests.CallAssertOriginCall() + cross(tests.CallAssertOriginCall)() }() // CallSubtestsAssertOriginCall(): panic @@ -42,10 +42,10 @@ func TestAssertOriginCall(t *testing.T) { t.Errorf("expected panic with '%v', got '%v'", expectedReason, r) } }() - if tests.CallSubtestsIsOriginCall() { + if cross(tests.CallSubtestsIsOriginCall)() { t.Errorf("expected IsOriginCall=false but got true") } - tests.CallSubtestsAssertOriginCall() + cross(tests.CallSubtestsAssertOriginCall)() } func TestPreviousRealm(t *testing.T) { @@ -54,12 +54,12 @@ func TestPreviousRealm(t *testing.T) { rTestsAddr = std.DerivePkgAddr("gno.land/r/demo/tests") ) // When only one realm in the frames, PreviousRealm returns the same realm - if addr := tests.GetPreviousRealm().Address(); addr != firstRealm { - println(tests.GetPreviousRealm()) + if addr := cross(tests.GetPreviousRealm)().Address(); addr != firstRealm { + println(cross(tests.GetPreviousRealm)()) t.Errorf("want GetPreviousRealm().Address==%s, got %s", firstRealm, addr) } // When 2 or more realms in the frames, PreviousRealm returns the second to last - if addr := tests.GetRSubtestsPreviousRealm().Address(); addr != rTestsAddr { + if addr := cross(tests.GetRSubtestsPreviousRealm)().Address(); addr != rTestsAddr { t.Errorf("want GetRSubtestsPreviousRealm().Address==%s, got %s", rTestsAddr, addr) } } diff --git a/examples/gno.land/r/demo/tests/z0_filetest.gno b/examples/gno.land/r/demo/tests/z0_filetest.gno index ff625d4906f..d321f53245c 100644 --- a/examples/gno.land/r/demo/tests/z0_filetest.gno +++ b/examples/gno.land/r/demo/tests/z0_filetest.gno @@ -5,24 +5,24 @@ import ( ) func main() { - println("tests.CallIsOriginCall:", tests.CallIsOriginCall()) - tests.CallAssertOriginCall() + println("tests.CallIsOriginCall:", cross(tests.CallIsOriginCall)()) + cross(tests.CallAssertOriginCall)() println("tests.CallAssertOriginCall doesn't panic when called directly") { // if called inside a block, this is no longer an origin call because // there's one additional frame (the block). - println("tests.CallIsOriginCall:", tests.CallIsOriginCall()) + println("tests.CallIsOriginCall:", cross(tests.CallIsOriginCall)()) defer func() { r := recover() println("tests.AssertOriginCall panics if when called inside a function literal:", r) }() - tests.CallAssertOriginCall() + cross(tests.CallAssertOriginCall)() } } // Output: -// tests.CallIsOriginCall: true +// tests.CallIsOriginCall: false // tests.CallAssertOriginCall doesn't panic when called directly -// tests.CallIsOriginCall: true +// tests.CallIsOriginCall: false // tests.AssertOriginCall panics if when called inside a function literal: undefined diff --git a/examples/gno.land/r/demo/tests/z1_filetest.gno b/examples/gno.land/r/demo/tests/z1_filetest.gno index 4873d69b96d..535a0fa2f3d 100644 --- a/examples/gno.land/r/demo/tests/z1_filetest.gno +++ b/examples/gno.land/r/demo/tests/z1_filetest.gno @@ -5,9 +5,9 @@ import ( ) func main() { - println(tests.Counter()) - tests.IncCounter() - println(tests.Counter()) + println(cross(tests.Counter)()) + cross(tests.IncCounter)() + println(cross(tests.Counter)()) } // Output: diff --git a/examples/gno.land/r/demo/tests/z2_filetest.gno b/examples/gno.land/r/demo/tests/z2_filetest.gno index 81d761c2b1b..94e9b51ee94 100644 --- a/examples/gno.land/r/demo/tests/z2_filetest.gno +++ b/examples/gno.land/r/demo/tests/z2_filetest.gno @@ -16,8 +16,8 @@ func main() { rTestsAddr = std.DerivePkgAddr("gno.land/r/demo/tests") ) testing.SetOriginCaller(eoa) - println("tests.GetPreviousRealm().Address(): ", tests.GetPreviousRealm().Address()) - println("tests.GetRSubtestsPreviousRealm().Address(): ", tests.GetRSubtestsPreviousRealm().Address()) + println("tests.GetPreviousRealm().Address(): ", cross(tests.GetPreviousRealm)().Address()) + println("tests.GetRSubtestsPreviousRealm().Address(): ", cross(tests.GetRSubtestsPreviousRealm)().Address()) } // Output: diff --git a/examples/gno.land/r/demo/tests/z3_filetest.gno b/examples/gno.land/r/demo/tests/z3_filetest.gno index cee2ea85201..0a54a649bef 100644 --- a/examples/gno.land/r/demo/tests/z3_filetest.gno +++ b/examples/gno.land/r/demo/tests/z3_filetest.gno @@ -16,14 +16,15 @@ func main() { ) testing.SetOriginCaller(eoa) // Contrarily to z2_filetest.gno we EXPECT GetPreviousRealms != eoa (#1704) - if addr := tests.GetPreviousRealm().Address(); addr != eoa { + if addr := cross(tests.GetPreviousRealm)().Address(); addr != eoa { println("want tests.GetPreviousRealm().Address ==", eoa, "got", addr) } // When 2 or more realms in the frames, it is also different - if addr := tests.GetRSubtestsPreviousRealm().Address(); addr != rTestsAddr { + if addr := cross(tests.GetRSubtestsPreviousRealm)().Address(); addr != rTestsAddr { println("want GetRSubtestsPreviousRealm().Address ==", rTestsAddr, "got", addr) } + println("Done.") } // Output: -// want tests.GetPreviousRealm().Address == g1wdhk6et0dej47h6lta047h6lta047h6lrnerlk got g1xufrdvnfk6zc9r0nqa23ld3tt2r5gkyvw76q63 +// Done. diff --git a/examples/gno.land/r/demo/wugnot/wugnot.gno b/examples/gno.land/r/demo/wugnot/wugnot.gno index 5d86e44f4f7..867c0cdc8ba 100644 --- a/examples/gno.land/r/demo/wugnot/wugnot.gno +++ b/examples/gno.land/r/demo/wugnot/wugnot.gno @@ -66,20 +66,14 @@ func Render(path string) string { } func TotalSupply() uint64 { - crossing() - return Token.TotalSupply() } func BalanceOf(owner std.Address) uint64 { - crossing() - return Token.BalanceOf(owner) } func Allowance(owner, spender std.Address) uint64 { - crossing() - return Token.Allowance(owner, spender) } diff --git a/examples/gno.land/r/demo/wugnot/z0_filetest.gno b/examples/gno.land/r/demo/wugnot/z0_filetest.gno index 205256f8b60..f9e6b00bb4e 100644 --- a/examples/gno.land/r/demo/wugnot/z0_filetest.gno +++ b/examples/gno.land/r/demo/wugnot/z0_filetest.gno @@ -13,25 +13,26 @@ import ( var ( addr1 = testutils.TestAddress("test1") addrc = std.DerivePkgAddr("gno.land/r/demo/wugnot") - addrt = std.DerivePkgAddr("gno.land/r/demo/wugnot_test") ) func main() { - testing.IssueCoins(addrc, std.Coins{{"ugnot", 100000001}}) // TODO: remove this - // issue ugnots testing.IssueCoins(addr1, std.Coins{{"ugnot", 100000001}}) - - // print initial state printBalances() // println(wugnot.Render("queues")) // println("A -", wugnot.Render("")) + // deposit of 123400ugnot from addr1 + // origin send must be simulated + coins := std.Coins{{"ugnot", 123_400}} testing.SetOriginCaller(addr1) - testing.SetOriginSend(std.Coins{{"ugnot", 123_400}}) - wugnot.Deposit() + testing.SetOriginSend(coins) + std.NewBanker(std.BankerTypeRealmSend).SendCoins(addr1, addrc, coins) + cross(wugnot.Deposit)() printBalances() - wugnot.Withdraw(4242) + + // withdraw of 4242ugnot to addr1 + cross(wugnot.Withdraw)(4242) printBalances() } @@ -41,11 +42,10 @@ func printBalances() { testing.SetOriginCaller(addr) robanker := std.NewBanker(std.BankerTypeReadonly) coins := robanker.GetCoins(addr).AmountOf("ugnot") - fmt.Printf("| %-13s | addr=%s | wugnot=%-5d | ugnot=%-9d |\n", + fmt.Printf("| %-13s | addr=%s | wugnot=%-6d | ugnot=%-9d |\n", name, addr, wugnotBal, coins) } println("-----------") - printSingleBalance("wugnot_test", addrt) printSingleBalance("wugnot", addrc) printSingleBalance("addr1", addr1) println("-----------") @@ -53,17 +53,14 @@ func printBalances() { // Output: // ----------- -// | wugnot_test | addr=g19rmydykafrqyyegc8uuaxxpzqwzcnxraj2dev9 | wugnot=0 | ugnot=0 | -// | wugnot | addr=g1pf6dv9fjk3rn0m4jjcne306ga4he3mzmupfjl6 | wugnot=0 | ugnot=100000001 | -// | addr1 | addr=g1w3jhxap3ta047h6lta047h6lta047h6l4mfnm7 | wugnot=0 | ugnot=100000001 | +// | wugnot | addr=g1pf6dv9fjk3rn0m4jjcne306ga4he3mzmupfjl6 | wugnot=0 | ugnot=0 | +// | addr1 | addr=g1w3jhxap3ta047h6lta047h6lta047h6l4mfnm7 | wugnot=0 | ugnot=100000001 | // ----------- // ----------- -// | wugnot_test | addr=g19rmydykafrqyyegc8uuaxxpzqwzcnxraj2dev9 | wugnot=123400 | ugnot=0 | -// | wugnot | addr=g1pf6dv9fjk3rn0m4jjcne306ga4he3mzmupfjl6 | wugnot=0 | ugnot=100000001 | -// | addr1 | addr=g1w3jhxap3ta047h6lta047h6lta047h6l4mfnm7 | wugnot=0 | ugnot=100000001 | +// | wugnot | addr=g1pf6dv9fjk3rn0m4jjcne306ga4he3mzmupfjl6 | wugnot=0 | ugnot=123400 | +// | addr1 | addr=g1w3jhxap3ta047h6lta047h6lta047h6l4mfnm7 | wugnot=123400 | ugnot=99876601 | // ----------- // ----------- -// | wugnot_test | addr=g19rmydykafrqyyegc8uuaxxpzqwzcnxraj2dev9 | wugnot=119158 | ugnot=4242 | -// | wugnot | addr=g1pf6dv9fjk3rn0m4jjcne306ga4he3mzmupfjl6 | wugnot=0 | ugnot=99995759 | -// | addr1 | addr=g1w3jhxap3ta047h6lta047h6lta047h6l4mfnm7 | wugnot=0 | ugnot=100000001 | +// | wugnot | addr=g1pf6dv9fjk3rn0m4jjcne306ga4he3mzmupfjl6 | wugnot=0 | ugnot=119158 | +// | addr1 | addr=g1w3jhxap3ta047h6lta047h6lta047h6l4mfnm7 | wugnot=119158 | ugnot=99880843 | // ----------- diff --git a/examples/gno.land/r/docs/minisocial/v1/admin.gno b/examples/gno.land/r/docs/minisocial/v1/admin.gno index 31a5bd95c7a..1c8fa90ea2c 100644 --- a/examples/gno.land/r/docs/minisocial/v1/admin.gno +++ b/examples/gno.land/r/docs/minisocial/v1/admin.gno @@ -6,6 +6,7 @@ var Ownable = ownable.NewWithAddress("g125em6arxsnj49vx35f0n0z34putv5ty3376fg5") // ResetPosts allows admin deletion of the posts func ResetPosts() { + crossing() Ownable.AssertOwnedByPrevious() posts = nil } diff --git a/examples/gno.land/r/docs/minisocial/v1/posts.gno b/examples/gno.land/r/docs/minisocial/v1/posts.gno index 3d1073b56a9..d4df4a06644 100644 --- a/examples/gno.land/r/docs/minisocial/v1/posts.gno +++ b/examples/gno.land/r/docs/minisocial/v1/posts.gno @@ -13,6 +13,7 @@ var posts []*Post // inefficient for large amounts of posts; see v2 // CreatePost creates a new post func CreatePost(text string) error { + crossing() // If the body of the post is empty, return an error if text == "" { return errors.New("empty post text") @@ -29,6 +30,7 @@ func CreatePost(text string) error { } func Render(_ string) string { + crossing() output := "# MiniSocial\n\n" // \n is needed just like in standard Markdown // Create a clickable link to create a post diff --git a/examples/gno.land/r/docs/minisocial/v1/posts_test.gno b/examples/gno.land/r/docs/minisocial/v1/posts_test.gno index 22d746d2491..1310300fa36 100644 --- a/examples/gno.land/r/docs/minisocial/v1/posts_test.gno +++ b/examples/gno.land/r/docs/minisocial/v1/posts_test.gno @@ -6,6 +6,7 @@ import ( "testing" "gno.land/p/demo/testutils" // Provides testing utilities + "gno.land/p/demo/uassert" ) func TestCreatePostSingle(t *testing.T) { @@ -15,21 +16,20 @@ func TestCreatePostSingle(t *testing.T) { testing.SetRealm(std.NewUserRealm(aliceAddr)) text1 := "Hello World!" - err := CreatePost(text1) - if err != nil { - t.Fatalf("expected no error, got %v", err) - } + err := cross(CreatePost)(text1) + uassert.True(t, err == nil, "expected no error") // Get the rendered page got := Render("") // Content should have the text and alice's address in it - if !(strings.Contains(got, text1) && strings.Contains(got, aliceAddr.String())) { - t.Fatal("expected render to contain text & alice's address") - } + condition := strings.Contains(got, text1) && strings.Contains(got, aliceAddr.String()) + uassert.True(t, condition, "expected render to contain text & alice's address") } func TestCreatePostMultiple(t *testing.T) { + testing.SetRealm(std.NewUserRealm(testutils.TestAddress("alice"))) + // Initialize a slice to hold the test posts and their authors posts := []struct { text string @@ -47,10 +47,8 @@ func TestCreatePostMultiple(t *testing.T) { testing.SetRealm(std.NewUserRealm(authorAddr)) // Create the post - err := CreatePost(p.text) - if err != nil { - t.Fatalf("expected no error for post '%s', got %v", p.text, err) - } + err := cross(CreatePost)(p.text) + uassert.True(t, err == nil, "expected no error for post "+p.text) } // Get the rendered page @@ -60,8 +58,7 @@ func TestCreatePostMultiple(t *testing.T) { for _, p := range posts { expectedText := p.text expectedAuthor := testutils.TestAddress(p.author).String() // Get the address for the author - if !(strings.Contains(got, expectedText) && strings.Contains(got, expectedAuthor)) { - t.Fatalf("expected render to contain text '%s' and address '%s'", expectedText, expectedAuthor) - } + condition := strings.Contains(got, expectedText) && strings.Contains(got, expectedAuthor) + uassert.True(t, condition, "expected render to contain text '"+expectedText+"' and address '"+expectedAuthor+"'") } } diff --git a/examples/gno.land/r/docs/minisocial/v2/admin.gno b/examples/gno.land/r/docs/minisocial/v2/admin.gno index 019c68925cd..5aaf307b5c6 100644 --- a/examples/gno.land/r/docs/minisocial/v2/admin.gno +++ b/examples/gno.land/r/docs/minisocial/v2/admin.gno @@ -11,6 +11,7 @@ var Ownable = ownable.NewWithAddress("g125em6arxsnj49vx35f0n0z34putv5ty3376fg5") // ResetPosts allows admin deletion of the posts func ResetPosts() { + crossing() Ownable.AssertOwnedByPrevious() posts = avl.NewTree() postID = seqid.ID(0) diff --git a/examples/gno.land/r/docs/minisocial/v2/posts.gno b/examples/gno.land/r/docs/minisocial/v2/posts.gno index ec5bab2faeb..080fb952702 100644 --- a/examples/gno.land/r/docs/minisocial/v2/posts.gno +++ b/examples/gno.land/r/docs/minisocial/v2/posts.gno @@ -29,6 +29,7 @@ var ( // CreatePost creates a new post func CreatePost(text string) error { + crossing() if text == "" { return ErrEmptyPost } @@ -55,6 +56,7 @@ func CreatePost(text string) error { // UpdatePost allows the author to update a post // The post can only be updated up to 10 minutes after posting func UpdatePost(id string, text string) error { + crossing() // Try to get the post raw, ok := posts.Get(id) if !ok { @@ -81,6 +83,7 @@ func UpdatePost(id string, text string) error { // DeletePost deletes a post with a specific id // Only the creator of a post can delete the post func DeletePost(id string) error { + crossing() // Try to get the post raw, ok := posts.Get(id) if !ok { @@ -106,6 +109,7 @@ func DeletePost(id string) error { // Render renders the main page of threads func Render(path string) string { + crossing() out := md.H1("MiniSocial") if posts.Size() == 0 { diff --git a/examples/gno.land/r/docs/minisocial/v2/posts_test.gno b/examples/gno.land/r/docs/minisocial/v2/posts_test.gno index c1bbf60a4c6..11cfde7e48d 100644 --- a/examples/gno.land/r/docs/minisocial/v2/posts_test.gno +++ b/examples/gno.land/r/docs/minisocial/v2/posts_test.gno @@ -6,6 +6,7 @@ import ( "testing" "gno.land/p/demo/testutils" // Provides testing utilities + "gno.land/p/demo/uassert" ) func TestCreatePostSingle(t *testing.T) { @@ -15,18 +16,15 @@ func TestCreatePostSingle(t *testing.T) { testing.SetRealm(std.NewUserRealm(aliceAddr)) text1 := "Hello World!" - err := CreatePost(text1) - if err != nil { - t.Fatalf("expected no error, got %v", err) - } + err := cross(CreatePost)(text1) + uassert.True(t, err == nil, "expected no error") // Get the rendered page got := Render("") // Content should have the text and alice's address in it - if !(strings.Contains(got, text1) && strings.Contains(got, aliceAddr.String())) { - t.Fatal("expected render to contain text & alice's address") - } + condition := strings.Contains(got, text1) && strings.Contains(got, aliceAddr.String()) + uassert.True(t, condition, "expected render to contain text & alice's address") } func TestCreatePostMultiple(t *testing.T) { @@ -47,10 +45,8 @@ func TestCreatePostMultiple(t *testing.T) { testing.SetRealm(std.NewUserRealm(authorAddr)) // Create the post - err := CreatePost(p.text) - if err != nil { - t.Fatalf("expected no error for post '%s', got %v", p.text, err) - } + err := cross(CreatePost)(p.text) + uassert.True(t, err == nil, "expected no error for post "+p.text) } // Get the rendered page @@ -60,9 +56,8 @@ func TestCreatePostMultiple(t *testing.T) { for _, p := range posts { expectedText := p.text expectedAuthor := testutils.TestAddress(p.author).String() // Get the address for the author - if !(strings.Contains(got, expectedText) && strings.Contains(got, expectedAuthor)) { - t.Fatalf("expected render to contain text '%s' and address '%s'", expectedText, expectedAuthor) - } + condition := strings.Contains(got, expectedText) && strings.Contains(got, expectedAuthor) + uassert.True(t, condition, "expected render to contain text '"+expectedText+"' and address '"+expectedAuthor+"'") } } @@ -71,33 +66,25 @@ func TestReset(t *testing.T) { testing.SetRealm(std.NewUserRealm(aliceAddr)) text1 := "Hello World!" - _ = CreatePost(text1) + _ = cross(CreatePost)(text1) got := Render("") - if !strings.Contains(got, text1) { - t.Fatal("expected render to contain text1") - } + uassert.True(t, strings.Contains(got, text1), "expected render to contain text1") // Set admin testing.SetRealm(std.NewUserRealm(Ownable.Owner())) - ResetPosts() + cross(ResetPosts)() got = Render("") - if strings.Contains(got, text1) { - t.Fatal("expected render to not contain text1") - } + uassert.False(t, strings.Contains(got, text1), "expected render to not contain text1") text2 := "Some other Text!!" - _ = CreatePost(text2) + _ = cross(CreatePost)(text2) got = Render("") - if strings.Contains(got, text1) { - t.Fatal("expected render to not contain text1") - } + uassert.False(t, strings.Contains(got, text1), "expected render to not contain text1") - if !strings.Contains(got, text2) { - t.Fatal("expected render to contain text2") - } + uassert.True(t, strings.Contains(got, text2), "expected render to contain text2") } // TODO: Add tests for Update & Delete diff --git a/examples/gno.land/r/gnoland/faucet/admin.gno b/examples/gno.land/r/gnoland/faucet/admin.gno index d991fcd9816..c44bec2d87e 100644 --- a/examples/gno.land/r/gnoland/faucet/admin.gno +++ b/examples/gno.land/r/gnoland/faucet/admin.gno @@ -6,6 +6,7 @@ import ( ) func AdminSetInPause(inPause bool) string { + crossing() if err := assertIsAdmin(); err != nil { return err.Error() } @@ -14,6 +15,7 @@ func AdminSetInPause(inPause bool) string { } func AdminSetMessage(message string) string { + crossing() if err := assertIsAdmin(); err != nil { return err.Error() } @@ -22,6 +24,7 @@ func AdminSetMessage(message string) string { } func AdminSetTransferLimit(amount int64) string { + crossing() if err := assertIsAdmin(); err != nil { return err.Error() } @@ -30,6 +33,7 @@ func AdminSetTransferLimit(amount int64) string { } func AdminSetAdminAddr(addr std.Address) string { + crossing() if err := assertIsAdmin(); err != nil { return err.Error() } @@ -38,6 +42,7 @@ func AdminSetAdminAddr(addr std.Address) string { } func AdminAddController(addr std.Address) string { + crossing() if err := assertIsAdmin(); err != nil { return err.Error() } @@ -58,6 +63,7 @@ func AdminAddController(addr std.Address) string { } func AdminRemoveController(addr std.Address) string { + crossing() if err := assertIsAdmin(); err != nil { return err.Error() } @@ -79,7 +85,7 @@ func AdminRemoveController(addr std.Address) string { } func assertIsAdmin() error { - caller := std.OriginCaller() + caller := std.PreviousRealm().Address() if caller != gAdminAddr { return errors.New("restricted for admin") } diff --git a/examples/gno.land/r/gnoland/faucet/faucet.gno b/examples/gno.land/r/gnoland/faucet/faucet.gno index a173c0423ee..8a71d85a5d6 100644 --- a/examples/gno.land/r/gnoland/faucet/faucet.gno +++ b/examples/gno.land/r/gnoland/faucet/faucet.gno @@ -21,10 +21,12 @@ var ( gTotalTransfers = uint(0) // per request limit, 350 gnot - gLimit std.Coin = std.NewCoin("ugnot", 350000000) + gLimit std.Coin = std.NewCoin("ugnot", 350_000_000) ) func Transfer(to std.Address, send int64) string { + crossing() + if err := assertIsController(); err != nil { return err.Error() } @@ -52,9 +54,14 @@ func GetPerTransferLimit() int64 { return gLimit.Amount } +func bankerAddr() std.Address { + crossing() + return std.CurrentRealm().Address() +} + func Render(_ string) string { - banker := std.NewBanker(std.BankerTypeRealmSend) - balance := banker.GetCoins(std.CurrentRealm().Address()) + banker := std.NewBanker(std.BankerTypeReadonly) + balance := banker.GetCoins(cross(bankerAddr)()) output := gMessage if gInPause { @@ -81,8 +88,7 @@ func Render(_ string) string { } func assertIsController() error { - caller := std.OriginCaller() - + caller := std.PreviousRealm().Address() ok := gControllers.Has(caller.String()) if !ok { return errors.New(caller.String() + " is not on the controller list") diff --git a/examples/gno.land/r/gnoland/faucet/faucet_test.gno b/examples/gno.land/r/gnoland/faucet/faucet_test.gno index 1d68ed11343..badac4d78f4 100644 --- a/examples/gno.land/r/gnoland/faucet/faucet_test.gno +++ b/examples/gno.land/r/gnoland/faucet/faucet_test.gno @@ -27,64 +27,63 @@ func TestPackage(t *testing.T) { test1addr = testutils.TestAddress("test1") ) // deposit 1000gnot to faucet contract - testing.IssueCoins(faucetaddr, std.Coins{{"ugnot", 1000000000}}) - assertBalance(t, faucetaddr, 1000000000) + testing.IssueCoins(faucetaddr, std.Coins{{"ugnot", 1_000_000_000}}) + assertBalance(t, faucetaddr, 1_000_000_000) // by default, balance is empty, and as a user I cannot call Transfer, or Admin commands. - assertBalance(t, test1addr, 0) - testing.SetOriginCaller(test1addr) - assertErr(t, faucet.Transfer(test1addr, 1000000)) + testing.SetRealm(std.NewUserRealm(test1addr)) + assertErr(t, cross(faucet.Transfer)(test1addr, 1_000_000)) - assertErr(t, faucet.AdminAddController(controlleraddr1)) - testing.SetOriginCaller(controlleraddr1) - assertErr(t, faucet.Transfer(test1addr, 1000000)) + assertErr(t, cross(faucet.AdminAddController)(controlleraddr1)) + testing.SetRealm(std.NewUserRealm(controlleraddr1)) + assertErr(t, cross(faucet.Transfer)(test1addr, 1_000_000)) // as an admin, add the controller to contract and deposit more 2000gnot to contract - testing.SetOriginCaller(adminaddr) - assertNoErr(t, faucet.AdminAddController(controlleraddr1)) - assertBalance(t, faucetaddr, 1000000000) + testing.SetRealm(std.NewUserRealm(adminaddr)) + assertNoErr(t, cross(faucet.AdminAddController)(controlleraddr1)) + assertBalance(t, faucetaddr, 1_000_000_000) // now, send some tokens as controller. - testing.SetOriginCaller(controlleraddr1) - assertNoErr(t, faucet.Transfer(test1addr, 1000000)) - assertBalance(t, test1addr, 1000000) - assertNoErr(t, faucet.Transfer(test1addr, 1000000)) - assertBalance(t, test1addr, 2000000) - assertBalance(t, faucetaddr, 998000000) + testing.SetRealm(std.NewUserRealm(controlleraddr1)) + assertNoErr(t, cross(faucet.Transfer)(test1addr, 1_000_000)) + assertBalance(t, test1addr, 1_000_000) + assertNoErr(t, cross(faucet.Transfer)(test1addr, 1_000_000)) + assertBalance(t, test1addr, 2_000_000) + assertBalance(t, faucetaddr, 998_000_000) // remove controller // as an admin, remove controller - testing.SetOriginCaller(adminaddr) - assertNoErr(t, faucet.AdminRemoveController(controlleraddr1)) - testing.SetOriginCaller(controlleraddr1) - assertErr(t, faucet.Transfer(test1addr, 1000000)) + testing.SetRealm(std.NewUserRealm(adminaddr)) + assertNoErr(t, cross(faucet.AdminRemoveController)(controlleraddr1)) + testing.SetRealm(std.NewUserRealm(controlleraddr1)) + assertErr(t, cross(faucet.Transfer)(test1addr, 1_000_000)) // duplicate controller - testing.SetOriginCaller(adminaddr) - assertNoErr(t, faucet.AdminAddController(controlleraddr1)) - assertErr(t, faucet.AdminAddController(controlleraddr1)) + testing.SetRealm(std.NewUserRealm(adminaddr)) + assertNoErr(t, cross(faucet.AdminAddController)(controlleraddr1)) + assertErr(t, cross(faucet.AdminAddController)(controlleraddr1)) // add more than more than allowed controllers - assertNoErr(t, faucet.AdminAddController(controlleraddr2)) - assertNoErr(t, faucet.AdminAddController(controlleraddr3)) - assertNoErr(t, faucet.AdminAddController(controlleraddr4)) - assertNoErr(t, faucet.AdminAddController(controlleraddr5)) - assertNoErr(t, faucet.AdminAddController(controlleraddr6)) - assertNoErr(t, faucet.AdminAddController(controlleraddr7)) - assertNoErr(t, faucet.AdminAddController(controlleraddr8)) - assertNoErr(t, faucet.AdminAddController(controlleraddr9)) - assertNoErr(t, faucet.AdminAddController(controlleraddr10)) - assertErr(t, faucet.AdminAddController(controlleraddr11)) + assertNoErr(t, cross(faucet.AdminAddController)(controlleraddr2)) + assertNoErr(t, cross(faucet.AdminAddController)(controlleraddr3)) + assertNoErr(t, cross(faucet.AdminAddController)(controlleraddr4)) + assertNoErr(t, cross(faucet.AdminAddController)(controlleraddr5)) + assertNoErr(t, cross(faucet.AdminAddController)(controlleraddr6)) + assertNoErr(t, cross(faucet.AdminAddController)(controlleraddr7)) + assertNoErr(t, cross(faucet.AdminAddController)(controlleraddr8)) + assertNoErr(t, cross(faucet.AdminAddController)(controlleraddr9)) + assertNoErr(t, cross(faucet.AdminAddController)(controlleraddr10)) + assertErr(t, cross(faucet.AdminAddController)(controlleraddr11)) // send more than per transfer limit - testing.SetOriginCaller(adminaddr) - faucet.AdminSetTransferLimit(300000000) - testing.SetOriginCaller(controlleraddr1) - assertErr(t, faucet.Transfer(test1addr, 301000000)) + testing.SetRealm(std.NewUserRealm(adminaddr)) + assertNoErr(t, cross(faucet.AdminSetTransferLimit)(300_000_000)) + testing.SetRealm(std.NewUserRealm(controlleraddr1)) + assertErr(t, cross(faucet.Transfer)(test1addr, 301_000_000)) // block transefer from the address not on the controllers list. - testing.SetOriginCaller(controlleraddr11) - assertErr(t, faucet.Transfer(test1addr, 1000000)) + testing.SetRealm(std.NewUserRealm(controlleraddr11)) + assertErr(t, cross(faucet.Transfer)(test1addr, 1_000_000)) } func assertErr(t *testing.T, err string) { diff --git a/examples/gno.land/r/gnoland/faucet/z0_filetest.gno b/examples/gno.land/r/gnoland/faucet/z0_filetest.gno index f913de85c82..067558725a3 100644 --- a/examples/gno.land/r/gnoland/faucet/z0_filetest.gno +++ b/examples/gno.land/r/gnoland/faucet/z0_filetest.gno @@ -9,8 +9,8 @@ import ( // mints ugnot to current realm func init() { - facuetaddr := std.DerivePkgAddr("gno.land/r/gnoland/faucet") - testing.IssueCoins(facuetaddr, std.Coins{{"ugnot", 200000000}}) + faucetaddr := std.DerivePkgAddr("gno.land/r/gnoland/faucet") + testing.IssueCoins(faucetaddr, std.Coins{{"ugnot", 200_000_000}}) } // assert render with empty path and no controllers @@ -22,10 +22,10 @@ func main() { // # Community Faucet. // // Status: active. -// Balance: . +// Balance: 200000000ugnot. // Total transfers: (in 0 times). // -// Package address: g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm +// Package address: g17rgsdnfxzza0sdfsdma37sdwxagsz378833ca4 // // Admin: g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5 // diff --git a/examples/gno.land/r/gnoland/faucet/z1_filetest.gno b/examples/gno.land/r/gnoland/faucet/z1_filetest.gno index 56b39cff359..1ffe978ab01 100644 --- a/examples/gno.land/r/gnoland/faucet/z1_filetest.gno +++ b/examples/gno.land/r/gnoland/faucet/z1_filetest.gno @@ -9,8 +9,8 @@ import ( // mints ugnot to current realm func init() { - facuetaddr := std.DerivePkgAddr("gno.land/r/gnoland/faucet") - testing.IssueCoins(facuetaddr, std.Coins{{"ugnot", 200000000}}) + faucetaddr := std.DerivePkgAddr("gno.land/r/gnoland/faucet") + testing.IssueCoins(faucetaddr, std.Coins{{"ugnot", 200_000_000}}) } // assert render with a path and no controllers @@ -22,10 +22,10 @@ func main() { // # Community Faucet. // // Status: active. -// Balance: . +// Balance: 200000000ugnot. // Total transfers: (in 0 times). // -// Package address: g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm +// Package address: g17rgsdnfxzza0sdfsdma37sdwxagsz378833ca4 // // Admin: g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5 // diff --git a/examples/gno.land/r/gnoland/faucet/z2_filetest.gno b/examples/gno.land/r/gnoland/faucet/z2_filetest.gno index 8cb4733c975..b2d8d49527d 100644 --- a/examples/gno.land/r/gnoland/faucet/z2_filetest.gno +++ b/examples/gno.land/r/gnoland/faucet/z2_filetest.gno @@ -10,8 +10,8 @@ import ( // mints ugnot to current realm func init() { - facuetaddr := std.DerivePkgAddr("gno.land/r/gnoland/faucet") - testing.IssueCoins(facuetaddr, std.Coins{{"ugnot", 200000000}}) + faucetaddr := std.DerivePkgAddr("gno.land/r/gnoland/faucet") + testing.IssueCoins(faucetaddr, std.Coins{{"ugnot", 200_000_000}}) } // assert render with empty path and 2 controllers @@ -21,12 +21,12 @@ func main() { controlleraddr1 = testutils.TestAddress("controller1") controlleraddr2 = testutils.TestAddress("controller2") ) - testing.SetOriginCaller(adminaddr) - err := faucet.AdminAddController(controlleraddr1) + testing.SetRealm(std.NewUserRealm(adminaddr)) + err := cross(faucet.AdminAddController)(controlleraddr1) if err != "" { panic(err) } - err = faucet.AdminAddController(controlleraddr2) + err = cross(faucet.AdminAddController)(controlleraddr2) if err != "" { panic(err) } @@ -37,7 +37,7 @@ func main() { // # Community Faucet. // // Status: active. -// Balance: . +// Balance: 200000000ugnot. // Total transfers: (in 0 times). // // Package address: g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5 diff --git a/examples/gno.land/r/gnoland/faucet/z3_filetest.gno b/examples/gno.land/r/gnoland/faucet/z3_filetest.gno index 76252d6b4a9..f4c55bbc928 100644 --- a/examples/gno.land/r/gnoland/faucet/z3_filetest.gno +++ b/examples/gno.land/r/gnoland/faucet/z3_filetest.gno @@ -10,8 +10,8 @@ import ( // mints coints to current realm func init() { - facuetaddr := std.DerivePkgAddr("gno.land/r/gnoland/faucet") - testing.IssueCoins(facuetaddr, std.Coins{{"ugnot", 200000000}}) + faucetaddr := std.DerivePkgAddr("gno.land/r/gnoland/faucet") + testing.IssueCoins(faucetaddr, std.Coins{{"ugnot", 200_000_000}}) } // assert render with 2 controllers and 2 transfers @@ -23,22 +23,22 @@ func main() { testaddr1 = testutils.TestAddress("test1") testaddr2 = testutils.TestAddress("test2") ) - testing.SetOriginCaller(adminaddr) - err := faucet.AdminAddController(controlleraddr1) + testing.SetRealm(std.NewUserRealm(adminaddr)) + err := cross(faucet.AdminAddController)(controlleraddr1) if err != "" { panic(err) } - err = faucet.AdminAddController(controlleraddr2) + err = cross(faucet.AdminAddController)(controlleraddr2) if err != "" { panic(err) } - testing.SetOriginCaller(controlleraddr1) - err = faucet.Transfer(testaddr1, 1000000) + testing.SetRealm(std.NewUserRealm(controlleraddr1)) + err = cross(faucet.Transfer)(testaddr1, 1_000_000) if err != "" { panic(err) } - testing.SetOriginCaller(controlleraddr2) - err = faucet.Transfer(testaddr1, 2000000) + testing.SetRealm(std.NewUserRealm(controlleraddr2)) + err = cross(faucet.Transfer)(testaddr1, 2_000_000) if err != "" { panic(err) } @@ -52,7 +52,7 @@ func main() { // Balance: 197000000ugnot. // Total transfers: 3000000ugnot (in 2 times). // -// Package address: g1ttrq7mp4zy6dssnmgyyktnn4hcj3ys8xhju0n7 +// Package address: g1vdhkuarjdakxcetjxf047h6lta047h6lnrev3v // // Admin: g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5 // @@ -61,5 +61,3 @@ func main() { // g1vdhkuarjdakxcetjx9047h6lta047h6lsdacav g1vdhkuarjdakxcetjxf047h6lta047h6lnrev3v // // Per request limit: 350000000ugnot -// -// diff --git a/examples/gno.land/r/gnoland/home/home.gno b/examples/gno.land/r/gnoland/home/home.gno index 6d8270832b9..d3e07b09e97 100644 --- a/examples/gno.land/r/gnoland/home/home.gno +++ b/examples/gno.land/r/gnoland/home/home.gno @@ -197,6 +197,7 @@ func quoteOfTheBlock() string { } func AdminSetOverride(content string) { + crossing() Admin.AssertOwnedByPrevious() override = content } diff --git a/examples/gno.land/r/gnoland/home/home_filetest.gno b/examples/gno.land/r/gnoland/home/home_filetest.gno index da3bb300293..de171a8f00f 100644 --- a/examples/gno.land/r/gnoland/home/home_filetest.gno +++ b/examples/gno.land/r/gnoland/home/home_filetest.gno @@ -19,7 +19,7 @@ func main() { // // Intuitive and easy to use, gno.land lowers the barrier to web3 and makes // censorship-resistant platforms accessible to everyone. If you want to help lay -// the foundations of a fairer and freer world, join us today. +// the foundations of a fairer and freer world, join us today. // // ## Learn about gno.land // diff --git a/examples/gno.land/r/gnoland/home/override_filetest.gno b/examples/gno.land/r/gnoland/home/override_filetest.gno index 846f60dfc55..c55508d14b5 100644 --- a/examples/gno.land/r/gnoland/home/override_filetest.gno +++ b/examples/gno.land/r/gnoland/home/override_filetest.gno @@ -11,15 +11,17 @@ import ( func main() { var admin = std.Address("g1manfred47kzduec920z88wfr64ylksmdcedlf5") testing.SetOriginCaller(admin) - home.AdminSetOverride("Hello World!") + cross(home.AdminSetOverride)("Hello World!") println("---") println(home.Render("")) - home.Admin.TransferOwnership(testutils.TestAddress("newAdmin")) + + newAdmin := testutils.TestAddress("newAdmin") + home.Admin.TransferOwnership(newAdmin) defer func() { r := recover() println("r: ", r) }() - home.AdminSetOverride("Not admin anymore") + cross(home.AdminSetOverride)("Not admin anymore") } // Output: diff --git a/examples/gno.land/r/gnoland/monit/monit.gno b/examples/gno.land/r/gnoland/monit/monit.gno index 1031565fd08..5353407c803 100644 --- a/examples/gno.land/r/gnoland/monit/monit.gno +++ b/examples/gno.land/r/gnoland/monit/monit.gno @@ -19,14 +19,15 @@ var ( counter int lastUpdate time.Time lastCaller std.Address - wd = watchdog.Watchdog{Duration: 5 * time.Minute} - Ownable = ownable.NewWithOrigin() watchdogDuration = 5 * time.Minute + wd = watchdog.Watchdog{Duration: watchdogDuration} + Ownable = ownable.NewWithOrigin() ) // Incr increments the counter and informs the watchdog that we're alive. // This function can be called by anyone. func Incr() int { + crossing() counter++ lastUpdate = time.Now() lastCaller = std.PreviousRealm().Address() @@ -37,12 +38,13 @@ func Incr() int { // Reset resets the realm state. // This function can only be called by the admin. func Reset() { + crossing() Ownable.AssertOwnedByPrevious() counter = 0 lastCaller = std.PreviousRealm().Address() lastUpdate = time.Now() - wd = watchdog.Watchdog{Duration: 5 * time.Minute} + wd = watchdog.Watchdog{Duration: watchdogDuration} } func Render(_ string) string { diff --git a/examples/gno.land/r/gnoland/monit/monit_test.gno b/examples/gno.land/r/gnoland/monit/monit_test.gno index fc9b394b8ed..b08f30cb632 100644 --- a/examples/gno.land/r/gnoland/monit/monit_test.gno +++ b/examples/gno.land/r/gnoland/monit/monit_test.gno @@ -1,12 +1,29 @@ package monit import ( + "std" "testing" + "time" + "gno.land/p/demo/ownable" "gno.land/p/demo/uassert" + "gno.land/p/demo/watchdog" ) +func initTest() { + counter = 0 + lastUpdate = time.Time{} + lastCaller = std.Address("") + wd = watchdog.Watchdog{Duration: watchdogDuration} + creator := std.Address("g1creator") + Ownable = ownable.NewWithAddress(creator) +} + func TestPackage(t *testing.T) { + initTest() + + testing.SetRealm(std.NewUserRealm("g1user")) + // initial state, watchdog is KO. { expected := `counter=0 @@ -18,13 +35,13 @@ status=KO` } // call Incr(), watchdog is OK. - Incr() - Incr() - Incr() + cross(Incr)() + cross(Incr)() + cross(Incr)() { expected := `counter=3 last update=2009-02-13 23:31:30 +0000 UTC m=+1234567890.000000001 -last caller=g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm +last caller=g1user status=OK` got := Render("") uassert.Equal(t, expected, got) @@ -54,3 +71,31 @@ status=OK` } */ } + +func TestReset(t *testing.T) { + initTest() + + // Initial state check + initialCounter := counter + initialLastUpdate := lastUpdate + initialLastCaller := lastCaller + initialStatus := wd.Status() + + // Call Incr to change the state + user := std.Address("g1user") + testing.SetRealm(std.NewUserRealm(user)) + cross(Incr)() + uassert.True(t, counter > initialCounter, "counter should have increased after Incr") + uassert.True(t, lastUpdate.After(initialLastUpdate), "lastUpdate should have been updated after Incr") + uassert.Equal(t, user, lastCaller, "lastCaller mismatch") + uassert.NotEqual(t, initialStatus, wd.Status(), "watchdog status should have changed after Incr") // Status changes after Alive() is called + + // Call Reset as the owner + ownerAddr := Ownable.Owner() + testing.SetRealm(std.NewUserRealm(ownerAddr)) // Simulate call from the owner + cross(Reset)() + uassert.Equal(t, 0, counter, "counter should be 0 after Reset") + uassert.Equal(t, ownerAddr, lastCaller, "lastCaller should be the owner address after Reset") + uassert.Equal(t, watchdogDuration.String(), wd.Duration.String(), "watchdog duration mismatch after Reset") + uassert.Equal(t, "KO", wd.Status(), "watchdog status should be KO after Reset") +} diff --git a/examples/gno.land/r/gnoland/users/v1/admin.gno b/examples/gno.land/r/gnoland/users/v1/admin.gno index e338e488aa5..315cf8b5784 100644 --- a/examples/gno.land/r/gnoland/users/v1/admin.gno +++ b/examples/gno.land/r/gnoland/users/v1/admin.gno @@ -9,10 +9,38 @@ import ( var paused = false // XXX: replace with p/moul/authz +//---------------------------------------- +// Privileged mutators. + +func setPaused(newPausedValue bool) { + crossing() + paused = newPausedValue +} + +func updateUsername(userData *susers.UserData, newName string) error { + crossing() + // UpdateName must be called from this realm. + return userData.UpdateName(newName) +} + +func deleteUserdata(userData *susers.UserData) error { + crossing() + // Delete must be called from this realm. + return userData.Delete() +} + +func setRegisterPrice(newPrice int64) { + crossing() + registerPrice = newPrice +} + +//---------------------------------------- +// Public API + // NewSetPausedExecutor allows GovDAO to pause or unpause this realm func NewSetPausedExecutor(newPausedValue bool) dao.ProposalRequest { cb := func() error { - paused = newPausedValue + cross(setPaused)(newPausedValue) return nil } @@ -35,7 +63,8 @@ func ProposeNewName(addr std.Address, newName string) dao.ProposalRequest { } cb := func() error { - return userData.UpdateName(newName) + err := cross(updateUsername)(userData, newName) + return err } e := dao.NewSimpleExecutor(cb, "") @@ -53,7 +82,7 @@ func ProposeDeleteUser(addr std.Address, desc string) dao.ProposalRequest { } cb := func() error { - return userData.Delete() + return cross(deleteUserdata)(userData) } e := dao.NewSimpleExecutor(cb, "") @@ -68,7 +97,7 @@ func ProposeNewRegisterPrice(newPrice int64) dao.ProposalRequest { } cb := func() error { - registerPrice = newPrice + cross(setRegisterPrice)(newPrice) 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 d717728d659..601983a1314 100644 --- a/examples/gno.land/r/gnoland/users/v1/users_test.gno +++ b/examples/gno.land/r/gnoland/users/v1/users_test.gno @@ -21,66 +21,65 @@ var ( func TestRegister_Valid(t *testing.T) { testing.SetOriginSend(std.NewCoins(std.NewCoin("ugnot", 1_000_000))) testing.SetRealm(std.NewUserRealm(aliceAddr)) - testing.SetOriginCaller(aliceAddr) uassert.NotPanics(t, func() { - Register(alice) + cross(Register)(alice) }) res, latest := susers.ResolveName(alice) - - uassert.NotEqual(t, nil, res) - uassert.Equal(t, alice, res.Name()) - uassert.Equal(t, aliceAddr, res.Addr()) - uassert.False(t, res.IsDeleted()) uassert.True(t, latest) + uassert.NotTypedNil(t, res) + if !istypednil(res) { + uassert.Equal(t, alice, res.Name()) + uassert.Equal(t, aliceAddr, res.Addr()) + uassert.False(t, res.IsDeleted()) + } } func TestRegister_Invalid(t *testing.T) { testing.SetOriginSend(std.NewCoins(std.NewCoin("ugnot", 1_000_000))) testing.SetRealm(std.NewUserRealm(bobAddr)) - testing.SetOriginCaller(bobAddr) // Invalid usernames uassert.PanicsWithMessage(t, ErrInvalidUsername.Error(), func() { - Register("alice") // vanity + cross(Register)("alice") // vanity }) uassert.PanicsWithMessage(t, ErrInvalidUsername.Error(), func() { - Register("") // empty + cross(Register)("") // empty }) uassert.PanicsWithMessage(t, ErrInvalidUsername.Error(), func() { - Register(" ") // empty + cross(Register)(" ") // empty }) uassert.PanicsWithMessage(t, ErrInvalidUsername.Error(), func() { - Register("123") // empty + cross(Register)("123") // empty }) uassert.PanicsWithMessage(t, ErrInvalidUsername.Error(), func() { - Register("123") // only numbers + cross(Register)("123") // only numbers }) uassert.PanicsWithMessage(t, ErrInvalidUsername.Error(), func() { - Register("alice&#($)") // non-allowed chars + cross(Register)("alice&#($)") // non-allowed chars }) uassert.PanicsWithMessage(t, ErrInvalidUsername.Error(), func() { - Register("Alice123") // upper-case + cross(Register)("Alice123") // upper-case }) uassert.PanicsWithMessage(t, ErrInvalidUsername.Error(), func() { - Register("toolongusernametoolongusernametoolongusername123") // too long + cross(Register)("toolongusernametoolongusernametoolongusername123") // too long }) // Name taken urequire.NotPanics(t, func() { - Register(bob) + cross(Register)(bob) }) uassert.PanicsWithMessage(t, susers.ErrNameTaken.Error(), func() { - Register(bob) // already registered + cross(Register)(bob) // already registered }) } @@ -91,6 +90,27 @@ func TestRegister_InvalidPayment(t *testing.T) { testing.SetOriginSend(std.NewCoins(std.NewCoin("ugnot", 12))) // invalid payment amount uassert.PanicsWithMessage(t, ErrInvalidPayment.Error(), func() { - Register(alice) + cross(Register)(alice) + }) +} + +func TestRegister_InvalidPayment(t *testing.T) { + testing.SetRealm(std.NewUserRealm(bobAddr)) + testing.SetOriginCaller(bobAddr) + + testing.SetOriginSend(std.NewCoins(std.NewCoin("ugnot", 12))) // invalid payment amount + + uassert.PanicsWithMessage(t, ErrInvalidPayment.Error(), func() { + cross(Register)(alice) + }) +} + +func TestUsers(t *testing.T) { + acc1 := std.Address("g1test1") + fee := "1000ugnot" + + // Check initial state + uassert.NotPanics(t, func() { + MustGetByName(susers.DefaultMinFee) }) } diff --git a/examples/gno.land/r/gnoland/users/v1/z_0_prop1_filetest.gno b/examples/gno.land/r/gnoland/users/v1/z_0_prop1_filetest.gno index dfed7c47e3f..c0c98b7da89 100644 --- a/examples/gno.land/r/gnoland/users/v1/z_0_prop1_filetest.gno +++ b/examples/gno.land/r/gnoland/users/v1/z_0_prop1_filetest.gno @@ -24,13 +24,13 @@ func init() { // Register alice testing.SetOriginCaller(alice) testing.SetRealm(std.NewUserRealm(alice)) - users.Register("alice123") + cross(users.Register)("alice123") // Prop to change name testing.SetOriginCaller(c) testing.SetRealm(std.NewUserRealm(c)) pr := users.ProposeNewName(alice, "alice_new123") - dao.MustCreateProposal(pr) + cross(dao.MustCreateProposal)(pr) } func main() { @@ -42,14 +42,17 @@ func main() { println(dao.Render("0")) println("--") - dao.MustVoteOnProposal(dao.VoteRequest{ + cross(dao.MustVoteOnProposal)(dao.VoteRequest{ Option: dao.YesVote, ProposalID: dao.ProposalID(0), }) + println("--") println(dao.Render("0")) println("--") - dao.ExecuteProposal(dao.ProposalID(0)) + + cross(dao.ExecuteProposal)(dao.ProposalID(0)) + println("--") println(dao.Render("0")) diff --git a/examples/gno.land/r/gnoland/users/v1/z_1_prop2_filetest.gno b/examples/gno.land/r/gnoland/users/v1/z_1_prop2_filetest.gno index 9d03bc4450c..e6bb2e10df4 100644 --- a/examples/gno.land/r/gnoland/users/v1/z_1_prop2_filetest.gno +++ b/examples/gno.land/r/gnoland/users/v1/z_1_prop2_filetest.gno @@ -25,14 +25,13 @@ func init() { // Register alice testing.SetOriginCaller(alice) testing.SetRealm(std.NewUserRealm(alice)) - users.Register("alice123") - - // Prop to change name - pr := users.ProposeDeleteUser(alice, "Change alice's name!") + cross(users.Register)("alice123") + // Prop to delete user testing.SetOriginCaller(c) testing.SetRealm(std.NewUserRealm(c)) - dao.MustCreateProposal(pr) + pr := users.ProposeDeleteUser(alice, "delete user test") + cross(dao.MustCreateProposal)(pr) } func main() { @@ -44,15 +43,16 @@ func main() { println(dao.Render("0")) println("--") - dao.MustVoteOnProposal(dao.VoteRequest{ + cross(dao.MustVoteOnProposal)(dao.VoteRequest{ Option: dao.YesVote, ProposalID: dao.ProposalID(0), }) + println("--") println(dao.Render("0")) println("--") - dao.ExecuteProposal(dao.ProposalID(0)) + cross(dao.ExecuteProposal)(dao.ProposalID(0)) println("--") println(dao.Render("0")) @@ -71,7 +71,7 @@ func main() { // // ### Proposed by: g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm // -// Change alice's name! +// delete user test // // // ### Proposal Status: @@ -89,7 +89,7 @@ func main() { // // ### Proposed by: g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm // -// Change alice's name! +// delete user test // // // ### Proposal Status: @@ -108,7 +108,7 @@ func main() { // // ### Proposed by: g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm // -// Change alice's name! +// delete user test // // // ### Proposal Status: @@ -127,7 +127,7 @@ func main() { // // ### Proposed by: g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm // -// Change alice's name! +// delete user test // // // ### Proposal Status: diff --git a/examples/gno.land/r/gnoland/users/z_0_filetest.gno b/examples/gno.land/r/gnoland/users/z_0_filetest.gno index 8afbeb78cbf..ff4dfaeb9e1 100644 --- a/examples/gno.land/r/gnoland/users/z_0_filetest.gno +++ b/examples/gno.land/r/gnoland/users/z_0_filetest.gno @@ -2,27 +2,37 @@ package main import ( "std" + "testing" + "gno.land/p/demo/testutils" "gno.land/r/gnoland/users" "gno.land/r/gov/dao" "gno.land/r/gov/dao/v3/init" ) +var ( + alice = testutils.TestAddress("g1alice") +) + func init() { + testing.SetRealm(std.NewUserRealm(alice)) c := std.OriginCaller() init.InitWithUsers(c) + pReq := users.ProposeNewRelease("gno.land/r/gnoland/users/v2", "This is a note!") - dao.MustCreateProposal(pReq) + cross(dao.MustCreateProposal)(pReq) } func main() { - dao.MustVoteOnProposal(dao.VoteRequest{ + testing.SetRealm(std.NewUserRealm(alice)) + + cross(dao.MustVoteOnProposal)(dao.VoteRequest{ Option: dao.YesVote, ProposalID: dao.ProposalID(0), }) - dao.ExecuteProposal(dao.ProposalID(0)) + cross(dao.ExecuteProposal)(dao.ProposalID(0)) println(users.Render("")) } diff --git a/examples/gno.land/r/gnoland/valopers/valopers.gno b/examples/gno.land/r/gnoland/valopers/valopers.gno index 9e9edcdf910..c4ad08f4748 100644 --- a/examples/gno.land/r/gnoland/valopers/valopers.gno +++ b/examples/gno.land/r/gnoland/valopers/valopers.gno @@ -54,23 +54,27 @@ func (v Valoper) Auth() *authorizable.Authorizable { } func AddToAuthList(address std.Address, member std.Address) { - v := GetByAddr(address) + crossing() - if err := v.Auth().AddToAuthList(member); err != nil { + v := GetByAddr(address) + if err := v.Auth().AddToAuthListByPrevious(member); err != nil { panic(err) } } func DeleteFromAuthList(address std.Address, member std.Address) { - v := GetByAddr(address) + crossing() - if err := v.Auth().DeleteFromAuthList(member); err != nil { + v := GetByAddr(address) + if err := v.Auth().DeleteFromAuthListByPrevious(member); err != nil { panic(err) } } // Register registers a new valoper func Register(moniker string, description string, address std.Address, pubKey string) { + crossing() + // Check if a fee is enforced if !minFee.IsZero() { sentCoins := std.OriginSend() @@ -92,7 +96,7 @@ func Register(moniker string, description string, address std.Address, pubKey st Address: address, PubKey: pubKey, KeepRunning: true, - auth: authorizable.NewAuthorizable(), + auth: authorizable.NewAuthorizableWithOrigin(), } if err := v.Validate(); err != nil { @@ -108,6 +112,8 @@ func Register(moniker string, description string, address std.Address, pubKey st // UpdateMoniker updates an existing valoper's moniker func UpdateMoniker(address std.Address, moniker string) { + crossing() + // Check that the moniker is not empty if err := validateMoniker(moniker); err != nil { panic(err) @@ -116,7 +122,7 @@ func UpdateMoniker(address std.Address, moniker string) { v := GetByAddr(address) // Check that the caller has permissions - v.Auth().AssertOnAuthList() + v.Auth().AssertPreviousOnAuthList() // Update the moniker v.Moniker = moniker @@ -127,6 +133,8 @@ func UpdateMoniker(address std.Address, moniker string) { // UpdateDescription updates an existing valoper's description func UpdateDescription(address std.Address, description string) { + crossing() + // Check that the description is not empty if err := validateDescription(description); err != nil { panic(err) @@ -135,7 +143,7 @@ func UpdateDescription(address std.Address, description string) { v := GetByAddr(address) // Check that the caller has permissions - v.Auth().AssertOnAuthList() + v.Auth().AssertPreviousOnAuthList() // Update the description v.Description = description @@ -146,10 +154,12 @@ func UpdateDescription(address std.Address, description string) { // UpdateKeepRunning updates an existing valoper's active status func UpdateKeepRunning(address std.Address, keepRunning bool) { + crossing() + v := GetByAddr(address) // Check that the caller has permissions - v.Auth().AssertOnAuthList() + v.Auth().AssertPreviousOnAuthList() // Update status v.KeepRunning = keepRunning diff --git a/examples/gno.land/r/gnoland/valopers/valopers_test.gno b/examples/gno.land/r/gnoland/valopers/valopers_test.gno index afe445c377c..3607d71031a 100644 --- a/examples/gno.land/r/gnoland/valopers/valopers_test.gno +++ b/examples/gno.land/r/gnoland/valopers/valopers_test.gno @@ -34,6 +34,9 @@ func validValidatorInfo(t *testing.T) struct { } func TestValopers_Register(t *testing.T) { + test1 := testutils.TestAddress("test1") + testing.SetRealm(std.NewUserRealm(test1)) + t.Run("already a valoper", func(t *testing.T) { // Clear the set for the test valopers = avl.NewTree() @@ -55,7 +58,7 @@ func TestValopers_Register(t *testing.T) { testing.SetOriginSend(std.Coins{minFee}) uassert.PanicsWithMessage(t, ErrValoperExists.Error(), func() { - Register(info.Moniker, info.Description, info.Address, info.PubKey) + cross(Register)(info.Moniker, info.Description, info.Address, info.PubKey) }) }) @@ -69,7 +72,7 @@ func TestValopers_Register(t *testing.T) { testing.SetOriginSend(std.Coins{std.NewCoin("ugnot", 0)}) uassert.PanicsWithMessage(t, ufmt.Sprintf("payment must not be less than %d%s", minFee.Amount, minFee.Denom), func() { - Register(info.Moniker, info.Description, info.Address, info.PubKey) + cross(Register)(info.Moniker, info.Description, info.Address, info.PubKey) }) }) @@ -83,7 +86,7 @@ func TestValopers_Register(t *testing.T) { testing.SetOriginSend(std.Coins{std.NewCoin("ugnot", minFee.Amount-1)}) uassert.PanicsWithMessage(t, ufmt.Sprintf("payment must not be less than %d%s", minFee.Amount, minFee.Denom), func() { - Register(info.Moniker, info.Description, info.Address, info.PubKey) + cross(Register)(info.Moniker, info.Description, info.Address, info.PubKey) }) }) @@ -97,7 +100,7 @@ func TestValopers_Register(t *testing.T) { testing.SetOriginSend(std.Coins{std.NewCoin("gnogno", minFee.Amount)}) uassert.PanicsWithMessage(t, "incompatible coin denominations: gnogno, ugnot", func() { - Register(info.Moniker, info.Description, info.Address, info.PubKey) + cross(Register)(info.Moniker, info.Description, info.Address, info.PubKey) }) }) @@ -111,7 +114,7 @@ func TestValopers_Register(t *testing.T) { testing.SetOriginSend(std.Coins{minFee}) uassert.NotPanics(t, func() { - Register(info.Moniker, info.Description, info.Address, info.PubKey) + cross(Register)(info.Moniker, info.Description, info.Address, info.PubKey) }) uassert.NotPanics(t, func() { @@ -140,19 +143,17 @@ func TestValopers_UpdateAuthMembers(t *testing.T) { testing.SetOriginSend(std.Coins{minFee}) testing.SetRealm(std.NewUserRealm(test1Address)) - testing.SetOriginCaller(test1Address) // TODO(bug, issue #2371): should not be needed // Add the valoper uassert.NotPanics(t, func() { - Register(info.Moniker, info.Description, info.Address, info.PubKey) + cross(Register)(info.Moniker, info.Description, info.Address, info.PubKey) }) testing.SetRealm(std.NewUserRealm(info.Address)) - testing.SetOriginCaller(info.Address) // TODO(bug, issue #2371): should not be needed // try to add member without being authorized uassert.PanicsWithMessage(t, authorizable.ErrNotSuperuser.Error(), func() { - AddToAuthList(info.Address, test2Address) + cross(AddToAuthList)(info.Address, test2Address) }) }) @@ -166,23 +167,22 @@ func TestValopers_UpdateAuthMembers(t *testing.T) { testing.SetOriginSend(std.Coins{minFee}) testing.SetRealm(std.NewUserRealm(test1Address)) - testing.SetOriginCaller(test1Address) // TODO(bug, issue #2371): should not be needed // Add the valoper uassert.NotPanics(t, func() { - Register(info.Moniker, info.Description, info.Address, info.PubKey) + cross(Register)(info.Moniker, info.Description, info.Address, info.PubKey) }) uassert.NotPanics(t, func() { - AddToAuthList(info.Address, test2Address) + // XXX this panics. + cross(AddToAuthList)(info.Address, test2Address) }) testing.SetRealm(std.NewUserRealm(info.Address)) - testing.SetOriginCaller(info.Address) // TODO(bug, issue #2371): should not be needed // try to add member without being authorized uassert.PanicsWithMessage(t, authorizable.ErrNotSuperuser.Error(), func() { - DeleteFromAuthList(info.Address, test2Address) + cross(DeleteFromAuthList)(info.Address, test2Address) }) }) @@ -196,24 +196,22 @@ func TestValopers_UpdateAuthMembers(t *testing.T) { testing.SetOriginSend(std.Coins{minFee}) testing.SetRealm(std.NewUserRealm(test1Address)) - testing.SetOriginCaller(test1Address) // TODO(bug, issue #2371): should not be needed // Add the valoper uassert.NotPanics(t, func() { - Register(info.Moniker, info.Description, info.Address, info.PubKey) + cross(Register)(info.Moniker, info.Description, info.Address, info.PubKey) }) uassert.NotPanics(t, func() { - AddToAuthList(info.Address, test2Address) + cross(AddToAuthList)(info.Address, test2Address) }) testing.SetRealm(std.NewUserRealm(test2Address)) - testing.SetOriginCaller(test2Address) // TODO(bug, issue #2371): should not be needed newMoniker := "new moniker" // Update the valoper uassert.NotPanics(t, func() { - UpdateMoniker(info.Address, newMoniker) + cross(UpdateMoniker)(info.Address, newMoniker) }) uassert.NotPanics(t, func() { @@ -235,7 +233,7 @@ func TestValopers_UpdateMoniker(t *testing.T) { // Update the valoper uassert.PanicsWithMessage(t, ErrValoperMissing.Error(), func() { - UpdateMoniker(info.Address, "new moniker") + cross(UpdateMoniker)(info.Address, "new moniker") }) }) @@ -253,7 +251,7 @@ func TestValopers_UpdateMoniker(t *testing.T) { // Add the valoper uassert.NotPanics(t, func() { - Register(info.Moniker, info.Description, info.Address, info.PubKey) + cross(Register)(info.Moniker, info.Description, info.Address, info.PubKey) }) // Change the origin caller @@ -261,7 +259,7 @@ func TestValopers_UpdateMoniker(t *testing.T) { // Update the valoper uassert.PanicsWithMessage(t, authorizable.ErrNotInAuthList.Error(), func() { - UpdateMoniker(info.Address, "new moniker") + cross(UpdateMoniker)(info.Address, "new moniker") }) }) @@ -281,18 +279,21 @@ func TestValopers_UpdateMoniker(t *testing.T) { info := validValidatorInfo(t) + // Set the origin caller + testing.SetOriginCaller(test1Address) + // Send coins testing.SetOriginSend(std.Coins{minFee}) // Add the valoper uassert.NotPanics(t, func() { - Register(info.Moniker, info.Description, info.Address, info.PubKey) + cross(Register)(info.Moniker, info.Description, info.Address, info.PubKey) }) for _, invalidMoniker := range invalidMonikers { // Update the valoper uassert.PanicsWithMessage(t, ErrInvalidMoniker.Error(), func() { - UpdateMoniker(info.Address, invalidMoniker) + cross(UpdateMoniker)(info.Address, invalidMoniker) }) } }) @@ -303,17 +304,20 @@ func TestValopers_UpdateMoniker(t *testing.T) { info := validValidatorInfo(t) + // Set the origin caller + testing.SetOriginCaller(test1Address) + // Send coins testing.SetOriginSend(std.Coins{minFee}) // Add the valoper uassert.NotPanics(t, func() { - Register(info.Moniker, info.Description, info.Address, info.PubKey) + cross(Register)(info.Moniker, info.Description, info.Address, info.PubKey) }) // Update the valoper uassert.PanicsWithMessage(t, ErrInvalidMoniker.Error(), func() { - UpdateMoniker(info.Address, strings.Repeat("a", MonikerMaxLength+1)) + cross(UpdateMoniker)(info.Address, strings.Repeat("a", MonikerMaxLength+1)) }) }) @@ -323,18 +327,21 @@ func TestValopers_UpdateMoniker(t *testing.T) { info := validValidatorInfo(t) + // Set the origin caller + testing.SetOriginCaller(test1Address) + // Send coins testing.SetOriginSend(std.Coins{minFee}) // Add the valoper uassert.NotPanics(t, func() { - Register(info.Moniker, info.Description, info.Address, info.PubKey) + cross(Register)(info.Moniker, info.Description, info.Address, info.PubKey) }) newMoniker := "new moniker" // Update the valoper uassert.NotPanics(t, func() { - UpdateMoniker(info.Address, newMoniker) + cross(UpdateMoniker)(info.Address, newMoniker) }) // Make sure the valoper is updated @@ -356,7 +363,7 @@ func TestValopers_UpdateDescription(t *testing.T) { // Update the valoper uassert.PanicsWithMessage(t, ErrValoperMissing.Error(), func() { - UpdateDescription(validValidatorInfo(t).Address, "new description") + cross(UpdateDescription)(validValidatorInfo(t).Address, "new description") }) }) @@ -374,7 +381,7 @@ func TestValopers_UpdateDescription(t *testing.T) { // Add the valoper uassert.NotPanics(t, func() { - Register(info.Moniker, info.Description, info.Address, info.PubKey) + cross(Register)(info.Moniker, info.Description, info.Address, info.PubKey) }) // Change the origin caller @@ -382,7 +389,7 @@ func TestValopers_UpdateDescription(t *testing.T) { // Update the valoper uassert.PanicsWithMessage(t, authorizable.ErrNotInAuthList.Error(), func() { - UpdateDescription(info.Address, "new description") + cross(UpdateDescription)(info.Address, "new description") }) }) @@ -392,18 +399,21 @@ func TestValopers_UpdateDescription(t *testing.T) { info := validValidatorInfo(t) + // Set the origin caller + testing.SetOriginCaller(test1Address) + // Send coins testing.SetOriginSend(std.Coins{minFee}) // Add the valoper uassert.NotPanics(t, func() { - Register(info.Moniker, info.Description, info.Address, info.PubKey) + cross(Register)(info.Moniker, info.Description, info.Address, info.PubKey) }) emptyDescription := "" // Update the valoper uassert.PanicsWithMessage(t, ErrInvalidDescription.Error(), func() { - UpdateDescription(info.Address, emptyDescription) + cross(UpdateDescription)(info.Address, emptyDescription) }) }) @@ -413,17 +423,20 @@ func TestValopers_UpdateDescription(t *testing.T) { info := validValidatorInfo(t) + // Set the origin caller + testing.SetOriginCaller(test1Address) + // Send coins testing.SetOriginSend(std.Coins{minFee}) // Add the valoper uassert.NotPanics(t, func() { - Register(info.Moniker, info.Description, info.Address, info.PubKey) + cross(Register)(info.Moniker, info.Description, info.Address, info.PubKey) }) // Update the valoper uassert.PanicsWithMessage(t, ErrInvalidDescription.Error(), func() { - UpdateDescription(info.Address, strings.Repeat("a", DescriptionMaxLength+1)) + cross(UpdateDescription)(info.Address, strings.Repeat("a", DescriptionMaxLength+1)) }) }) @@ -433,18 +446,21 @@ func TestValopers_UpdateDescription(t *testing.T) { info := validValidatorInfo(t) + // Set the origin caller + testing.SetOriginCaller(test1Address) + // Send coins testing.SetOriginSend(std.Coins{minFee}) // Add the valoper uassert.NotPanics(t, func() { - Register(info.Moniker, info.Description, info.Address, info.PubKey) + cross(Register)(info.Moniker, info.Description, info.Address, info.PubKey) }) newDescription := "new description" // Update the valoper uassert.NotPanics(t, func() { - UpdateDescription(info.Address, newDescription) + cross(UpdateDescription)(info.Address, newDescription) }) // Make sure the valoper is updated @@ -466,7 +482,7 @@ func TestValopers_UpdateKeepRunning(t *testing.T) { // Update the valoper uassert.PanicsWithMessage(t, ErrValoperMissing.Error(), func() { - UpdateKeepRunning(validValidatorInfo(t).Address, false) + cross(UpdateKeepRunning)(validValidatorInfo(t).Address, false) }) }) @@ -484,7 +500,7 @@ func TestValopers_UpdateKeepRunning(t *testing.T) { // Add the valoper uassert.NotPanics(t, func() { - Register(info.Moniker, info.Description, info.Address, info.PubKey) + cross(Register)(info.Moniker, info.Description, info.Address, info.PubKey) }) // Change the origin caller @@ -492,7 +508,7 @@ func TestValopers_UpdateKeepRunning(t *testing.T) { // Update the valoper uassert.PanicsWithMessage(t, authorizable.ErrNotInAuthList.Error(), func() { - UpdateKeepRunning(info.Address, false) + cross(UpdateKeepRunning)(info.Address, false) }) }) @@ -502,17 +518,20 @@ func TestValopers_UpdateKeepRunning(t *testing.T) { info := validValidatorInfo(t) + // Set the origin caller + testing.SetOriginCaller(test1Address) + // Send coins testing.SetOriginSend(std.Coins{minFee}) // Add the valoper uassert.NotPanics(t, func() { - Register(info.Moniker, info.Description, info.Address, info.PubKey) + cross(Register)(info.Moniker, info.Description, info.Address, info.PubKey) }) // Update the valoper uassert.NotPanics(t, func() { - UpdateKeepRunning(info.Address, false) + cross(UpdateKeepRunning)(info.Address, false) }) // Make sure the valoper is updated diff --git a/examples/gno.land/r/gnoland/valopers/z_1_filetest.gno b/examples/gno.land/r/gnoland/valopers/z_1_filetest.gno index ce6556c2ecf..6216b5227bc 100644 --- a/examples/gno.land/r/gnoland/valopers/z_1_filetest.gno +++ b/examples/gno.land/r/gnoland/valopers/z_1_filetest.gno @@ -1,14 +1,20 @@ // PKGPATH: gno.land/r/gnoland/valopers_test -package valopers_test - // SEND: 20000000ugnot +package valopers_test + import ( "std" + "testing" + "gno.land/p/demo/testutils" "gno.land/r/gnoland/valopers" ) +var ( + g1user = testutils.TestAddress("g1user") // g1vuch2um9wf047h6lta047h6lta047h6l2ewm6w +) + const ( validMoniker = "test-1" validDescription = "test-1's description" @@ -17,8 +23,10 @@ const ( ) func init() { + testing.SetOriginCaller(g1user) + // Register a validator and add the proposal - valopers.Register(validMoniker, validDescription, validAddress, validPubKey) + cross(valopers.Register)(validMoniker, validDescription, validAddress, validPubKey) } func main() { @@ -96,6 +104,6 @@ func main() { // // // -// * [test-1](/r/gnoland/valopers:g1sp8v98h2gadm5jggtzz9w5ksexqn68ympsd68h) - [profile](/r/demo/profile:u/g1p9elmfxvctlkypargf7wruch5vchuysqr2xg2q) +// * [test-1](/r/gnoland/valopers:g1sp8v98h2gadm5jggtzz9w5ksexqn68ympsd68h) - [profile](/r/demo/profile:u/g1vuch2um9wf047h6lta047h6lta047h6l2ewm6w) // // diff --git a/examples/gno.land/r/gnoland/valopers/z_2_filetest.gno b/examples/gno.land/r/gnoland/valopers/z_2_filetest.gno index 8aad542dd88..d1d6e7244f1 100644 --- a/examples/gno.land/r/gnoland/valopers/z_2_filetest.gno +++ b/examples/gno.land/r/gnoland/valopers/z_2_filetest.gno @@ -1,14 +1,20 @@ // PKGPATH: gno.land/r/gnoland/valopers_test -package valopers_test - // SEND: 20000000ugnot +package valopers_test + import ( "std" + "testing" + "gno.land/p/demo/testutils" "gno.land/r/gnoland/valopers" ) +var ( + g1user = testutils.TestAddress("g1user") +) + const ( validMoniker = "test-1" validDescription = "test-1's description" @@ -17,8 +23,10 @@ const ( ) func init() { + testing.SetOriginCaller(g1user) + // Register a validator and add the proposal - valopers.Register(validMoniker, validDescription, validAddress, validPubKey) + cross(valopers.Register)(validMoniker, validDescription, validAddress, validPubKey) } func main() { diff --git a/examples/gno.land/r/gnoland/valopers_proposal/proposal_test.gno b/examples/gno.land/r/gnoland/valopers_proposal/proposal_test.gno index 73c7f314927..03eeafba841 100644 --- a/examples/gno.land/r/gnoland/valopers_proposal/proposal_test.gno +++ b/examples/gno.land/r/gnoland/valopers_proposal/proposal_test.gno @@ -9,12 +9,15 @@ import ( "gno.land/p/demo/urequire" "gno.land/r/gnoland/valopers" "gno.land/r/gov/dao" - "gno.land/r/gov/dao/v3/init" // so that the govdao initializer is executed + daoinit "gno.land/r/gov/dao/v3/init" // so that the govdao initializer is executed +) + +var ( + g1user = testutils.TestAddress("g1user") ) func init() { - c := std.OriginCaller() - init.InitWithUsers(c) + daoinit.InitWithUsers(g1user) } func TestValopers_ProposeNewValidator(t *testing.T) { @@ -27,30 +30,31 @@ func TestValopers_ProposeNewValidator(t *testing.T) { pubKey = "gpub1pggj7ard9eg82cjtv4u52epjx56nzwgjyg9zqwpdwpd0f9fvqla089ndw5g9hcsufad77fml2vlu73fk8q8sh8v72cza5p" ) - test1Address := testutils.TestAddress("test1") + // Set origin caller + testing.SetRealm(std.NewUserRealm(g1user)) t.Run("remove an unexisting validator", func(t *testing.T) { // Send coins to be able to register a valoper testing.SetOriginSend(std.Coins{std.NewCoin("ugnot", registerMinFee)}) urequire.NotPanics(t, func() { - valopers.Register(moniker, description, test1Address, pubKey) - valopers.UpdateKeepRunning(test1Address, false) + cross(valopers.Register)(moniker, description, g1user, pubKey) + cross(valopers.UpdateKeepRunning)(g1user, false) }) var valoper valopers.Valoper urequire.NotPanics(t, func() { - valoper = valopers.GetByAddr(test1Address) + valoper = valopers.GetByAddr(g1user) }) // Send coins to be able to make a proposal testing.SetOriginSend(std.Coins{std.NewCoin("ugnot", proposalMinFee)}) urequire.PanicsWithMessage(t, ErrValidatorMissing.Error(), func() { - pr := NewValidatorProposalRequest(test1Address) + pr := NewValidatorProposalRequest(g1user) - dao.MustCreateProposal(pr) + cross(dao.MustCreateProposal)(pr) }) }) @@ -59,13 +63,13 @@ func TestValopers_ProposeNewValidator(t *testing.T) { testing.SetOriginSend(std.Coins{std.NewCoin("ugnot", registerMinFee)}) urequire.NotPanics(t, func() { - valopers.UpdateKeepRunning(test1Address, true) + cross(valopers.UpdateKeepRunning)(g1user, true) }) var valoper valopers.Valoper urequire.NotPanics(t, func() { - valoper = valopers.GetByAddr(test1Address) + valoper = valopers.GetByAddr(g1user) }) // Send coins to be able to make a proposal @@ -73,12 +77,12 @@ func TestValopers_ProposeNewValidator(t *testing.T) { var pid dao.ProposalID urequire.NotPanics(t, func() { - pr := NewValidatorProposalRequest(test1Address) + pr := NewValidatorProposalRequest(g1user) - pid := dao.MustCreateProposal(pr) + pid := cross(dao.MustCreateProposal)(pr) }) - proposal, err := dao.GetProposal(pid) // index starts from 0 + proposal, err := cross(dao.GetProposal)(pid) // index starts from 0 urequire.NoError(t, err, "proposal not found") description := ufmt.Sprintf("Valoper profile: [%s](/r/gnoland/valopers:%s)\n\n%s", @@ -98,27 +102,27 @@ func TestValopers_ProposeNewValidator(t *testing.T) { var valoper valopers.Valoper urequire.NotPanics(t, func() { - valoper = valopers.GetByAddr(test1Address) + valoper = valopers.GetByAddr(g1user) }) urequire.NotPanics(t, func() { // Vote the proposal created in the previous test - dao.MustVoteOnProposal(dao.VoteRequest{ + cross(dao.MustVoteOnProposal)(dao.VoteRequest{ Option: dao.YesVote, ProposalID: dao.ProposalID(0), }) // Execute the proposal - dao.ExecuteProposal(dao.ProposalID(0)) + cross(dao.ExecuteProposal)(dao.ProposalID(0)) }) // Send coins to be able to make a proposal testing.SetOriginSend(std.Coins{std.NewCoin("ugnot", proposalMinFee)}) urequire.PanicsWithMessage(t, ErrSameValues.Error(), func() { - pr := NewValidatorProposalRequest(test1Address) + pr := NewValidatorProposalRequest(g1user) - dao.MustCreateProposal(pr) + cross(dao.MustCreateProposal)(pr) }) }) } @@ -129,6 +133,9 @@ func TestValopers_ProposeNewInstructions(t *testing.T) { newInstructions := "new instructions" description := ufmt.Sprintf("Update the instructions to: \n\n%s", newInstructions) + // Set origin caller + testing.SetRealm(std.NewUserRealm(g1user)) + // Send coins to be able to make a proposal testing.SetOriginSend(std.Coins{std.NewCoin("ugnot", proposalMinFee)}) @@ -136,10 +143,10 @@ func TestValopers_ProposeNewInstructions(t *testing.T) { urequire.NotPanics(t, func() { pr := ProposeNewInstructionsProposalRequest(newInstructions) - pid = dao.MustCreateProposal(pr) + pid = cross(dao.MustCreateProposal)(pr) }) - proposal, err := dao.GetProposal(pid) // index starts from 0 + proposal, err := cross(dao.GetProposal)(pid) // index starts from 0 urequire.NoError(t, err, "proposal not found") if proposal == nil { panic("PROPOSAL NOT FOUND") @@ -154,6 +161,9 @@ func TestValopers_ProposeNewMinFee(t *testing.T) { newMinFee := int64(10) description := ufmt.Sprintf("Update the minimum register fee to: %d ugnot", newMinFee) + // Set origin caller + testing.SetRealm(std.NewUserRealm(g1user)) + // Send coins to be able to make a proposal testing.SetOriginSend(std.Coins{std.NewCoin("ugnot", proposalMinFee)}) @@ -161,11 +171,71 @@ func TestValopers_ProposeNewMinFee(t *testing.T) { urequire.NotPanics(t, func() { pr := ProposeNewMinFeeProposalRequest(newMinFee) - pid = dao.MustCreateProposal(pr) + pid = cross(dao.MustCreateProposal)(pr) }) - proposal, err := dao.GetProposal(pid) // index starts from 0 + proposal, err := cross(dao.GetProposal)(pid) // index starts from 0 urequire.NoError(t, err, "proposal not found") // Check that the proposal is correct urequire.Equal(t, description, proposal.Description()) } + +func TestValopers_ProposeNewValidator(t *testing.T) { + const ( + registerMinFee int64 = 20 * 1_000_000 // minimum gnot must be paid to register. + proposalMinFee int64 = 100 * 1_000_000 + + moniker string = "moniker" + description string = "description" + pubKey = "gpub1pggj7ard9eg82cjtv4u52epjx56nzwgjyg9zqwpdwpd0f9fvqla089ndw5g9hcsufad77fml2vlu73fk8q8sh8v72cza5p" + ) + + // Set origin caller + testing.SetRealm(std.NewUserRealm(g1user)) + + t.Run("create valid proposal", func(t *testing.T) { + // Validator exists, should not panic + urequire.NotPanics(t, func() { + _ = valopers.MustGetValoper(g1user) + }) + + // Create the proposal + urequire.NotPanics(t, func() { + cross(valopers.Register)(moniker, description, g1user, pubKey) + }) + + // Verify proposal details + urequire.NotPanics(t, func() { + valoper := valopers.MustGetValoper(g1user) + urequire.Equal(t, moniker, valoper.Moniker) + urequire.Equal(t, description, valoper.Description) + }) + // Execute proposal with admin rights + urequire.NotPanics(t, func() { + std.TestSetOrigCaller(std.Admin) + cross(dao.ExecuteProposal)(dao.ProposalID(0)) + }) + // Check if valoper was updated + urequire.NotPanics(t, func() { + valoper := valopers.MustGetValoper(g1user) + urequire.Equal(t, moniker, valoper.Moniker) + urequire.Equal(t, description, valoper.Description) + }) + + // Expect ExecuteProposal to pass + urequire.NotPanics(t, func() { + cross(dao.ExecuteProposal)(dao.ProposalID(0)) + }) + // Check if valoper was updated + urequire.NotPanics(t, func() { + valoper := valopers.MustGetValoper(g1user) + urequire.Equal(t, moniker, valoper.Moniker) + urequire.Equal(t, description, valoper.Description) + }) + // Execute proposal with admin rights + urequire.NotPanics(t, func() { + std.TestSetOrigCaller(std.Admin) + cross(dao.ExecuteProposal)(dao.ProposalID(0)) + }) + }) +} diff --git a/examples/gno.land/r/gnoland/valopers_proposal/z_0_a_filetest.gno b/examples/gno.land/r/gnoland/valopers_proposal/z_0_a_filetest.gno index 02c616a5e97..f05e3bf9e12 100644 --- a/examples/gno.land/r/gnoland/valopers_proposal/z_0_a_filetest.gno +++ b/examples/gno.land/r/gnoland/valopers_proposal/z_0_a_filetest.gno @@ -1,15 +1,21 @@ // PKGPATH: gno.land/r/gnoland/valopers_proposal_test -package valopers_proposal_test - // SEND: 20000000ugnot +package valopers_proposal_test + import ( "std" + "testing" + "gno.land/p/demo/testutils" "gno.land/r/gnoland/valopers" "gno.land/r/gnoland/valopers_proposal" "gno.land/r/gov/dao" - "gno.land/r/gov/dao/v3/init" + daoinit "gno.land/r/gov/dao/v3/init" +) + +var ( + g1user = testutils.TestAddress("g1user") ) const ( @@ -21,13 +27,14 @@ const ( ) func init() { - c := std.OriginCaller() - init.InitWithUsers(c) + testing.SetOriginCaller(g1user) + daoinit.InitWithUsers(g1user) } func main() { + testing.SetOriginCaller(g1user) // Register a validator - valopers.Register(validMoniker, validDescription, validAddress, validPubKey) + cross(valopers.Register)(validMoniker, validDescription, validAddress, validPubKey) // Try to make a proposal for a non-existing validator defer func() { r := recover() @@ -35,7 +42,7 @@ func main() { }() pr := valopers_proposal.NewValidatorProposalRequest(otherAddress) - dao.MustCreateProposal(pr) + cross(dao.MustCreateProposal)(pr) } // Output: diff --git a/examples/gno.land/r/gnoland/valopers_proposal/z_1_filetest.gno b/examples/gno.land/r/gnoland/valopers_proposal/z_1_filetest.gno index d84d73c4a3e..22723c38a50 100644 --- a/examples/gno.land/r/gnoland/valopers_proposal/z_1_filetest.gno +++ b/examples/gno.land/r/gnoland/valopers_proposal/z_1_filetest.gno @@ -1,16 +1,21 @@ // PKGPATH: gno.land/r/gnoland/valopers_proposal_test -package valopers_proposal_test - // SEND: 100000000ugnot +package valopers_proposal_test + import ( "std" "testing" + "gno.land/p/demo/testutils" "gno.land/r/gnoland/valopers" "gno.land/r/gnoland/valopers_proposal" "gno.land/r/gov/dao" - "gno.land/r/gov/dao/v3/init" // so that the govdao initializer is executed + daoinit "gno.land/r/gov/dao/v3/init" // so that the govdao initializer is executed +) + +var ( + g1user = testutils.TestAddress("g1user") // g1vuch2um9wf047h6lta047h6lta047h6l2ewm6w ) const ( @@ -21,15 +26,14 @@ const ( ) func init() { - c := std.OriginCaller() - init.InitWithUsers(c) + testing.SetOriginCaller(g1user) + daoinit.InitWithUsers(g1user) // Register a validator and add the proposal - valopers.Register(validMoniker, validDescription, validAddress, validPubKey) + cross(valopers.Register)(validMoniker, validDescription, validAddress, validPubKey) pr := valopers_proposal.NewValidatorProposalRequest(validAddress) - testing.SetOriginCaller(c) - dao.MustCreateProposal(pr) + cross(dao.MustCreateProposal)(pr) } func main() { @@ -41,7 +45,7 @@ func main() { // ## Proposal with id: 0 // ### Title: Add valoper test-1 to the valset // -// ### Proposed by: g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm +// ### Proposed by: g1vuch2um9wf047h6lta047h6lta047h6l2ewm6w // // Valoper profile: [test-1](/r/gnoland/valopers:g1sp8v98h2gadm5jggtzz9w5ksexqn68ympsd68h) // diff --git a/examples/gno.land/r/gov/dao/proxy.gno b/examples/gno.land/r/gov/dao/proxy.gno index 9bb28f5dea4..4b6a93d1382 100644 --- a/examples/gno.land/r/gov/dao/proxy.gno +++ b/examples/gno.land/r/gov/dao/proxy.gno @@ -15,10 +15,13 @@ var AllowedDAOs []string // proposals contains all the proposals in history. var proposals *Proposals = NewProposals() +// Remember this realm for rendering. +var realm = std.CurrentRealm() + // Render calls directly to Render's DAO implementation. // This allows to have this realm as the main entry point for everything. func Render(p string) string { - return dao.Render(context(), p) + return dao.Render(realm.PkgPath(), p) } // MustCreateProposal is an utility method that does the same as CreateProposal, @@ -40,7 +43,7 @@ func MustCreateProposal(r ProposalRequest) ProposalID { func ExecuteProposal(pid ProposalID) bool { crossing() - execute, err := dao.PreExecuteProposal(context(), pid) + execute, err := dao.PreExecuteProposal(pid) if err != nil { panic(err.Error()) } @@ -63,7 +66,7 @@ func ExecuteProposal(pid ProposalID) bool { func CreateProposal(r ProposalRequest) (ProposalID, error) { crossing() - author, err := dao.PreCreateProposal(context(), r) + author, err := dao.PreCreateProposal(r) if err != nil { return -1, err } @@ -77,7 +80,7 @@ func CreateProposal(r ProposalRequest) (ProposalID, error) { } pid := proposals.SetProposal(p) - dao.PostCreateProposal(context(), r, pid) + dao.PostCreateProposal(r, pid) return pid, nil } @@ -96,7 +99,7 @@ func MustVoteOnProposal(r VoteRequest) { func VoteOnProposal(r VoteRequest) error { crossing() - return dao.VoteOnProposal(context(), r) + return dao.VoteOnProposal(r) } // MustVoteOnProposalSimple is like MustVoteOnProposal but intended to be used through gnokey with basic types. @@ -124,13 +127,13 @@ func MustGetProposal(pid ProposalID) *Proposal { func GetProposal(pid ProposalID) (*Proposal, error) { crossing() - if err := dao.PreGetProposal(context(), pid); err != nil { + if err := dao.PreGetProposal(pid); err != nil { return nil, err } prop := proposals.GetProposal(pid) - if err := dao.PostGetProposal(context(), pid, prop); err != nil { + if err := dao.PostGetProposal(pid, prop); err != nil { return nil, err } @@ -163,8 +166,6 @@ func UpdateImpl(r UpdateRequest) { } func InAllowedDAOs(pkg string) bool { - crossing() - if len(AllowedDAOs) == 0 { return true // corner case for initialization } @@ -175,10 +176,3 @@ func InAllowedDAOs(pkg string) bool { } return false } - -func context() *Context { - return &Context{ - PrevRealm: std.PreviousRealm(), - CurrentRealm: std.CurrentRealm(), - } -} diff --git a/examples/gno.land/r/gov/dao/proxy_test.gno b/examples/gno.land/r/gov/dao/proxy_test.gno index 74d2596a692..c2cc3ccfe70 100644 --- a/examples/gno.land/r/gov/dao/proxy_test.gno +++ b/examples/gno.land/r/gov/dao/proxy_test.gno @@ -85,11 +85,12 @@ func TestProxy_Functions(t *testing.T) { if p != nil { panic("proposal must be nil") } - p = cross(MustGetProposal)(pid) urequire.Equal(t, "Proposal Title", p.Title()) - urequire.Equal(t, p.Author().String(), "g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm") + urequire.Equal(t, p.Author().String(), alice.String()) + // need to switch the context back to v4 + testing.SetRealm(std.NewCodeRealm(v4)) urequire.Equal( t, "Render: test", @@ -102,6 +103,33 @@ func TestProxy_Functions(t *testing.T) { DAO: &dummyDao{}, AllowedDAOs: []string{}, }) + + t.Run("set valid impl realm path by owner", func(t *testing.T) { + implRealmPath := "gno.land/r/gov/dao/v3/impl" // Valid realm path + + // Should not panic + urequire.NotPanics(t, func() { + SetImplRealmPath(implRealmPath) + }) + + // Check if path was set correctly + urequire.NotPanics(t, func() { + urequire.Equal(t, implRealmPath, GetImplRealmPath()) + }) + // Get should also work + urequire.NotPanics(t, func() { + urequire.Equal(t, implRealmPath, GetImplRealmPath()) + }) + }) + + t.Run("set new valid impl realm path by owner", func(t *testing.T) { + implRealmPath := "gno.land/r/gov/dao/v2/impl" // Valid realm path + + // Should not panic + urequire.NotPanics(t, func() { + SetImplRealmPath(implRealmPath) + }) + }) } type dummyDao struct { diff --git a/examples/gno.land/r/gov/dao/types.gno b/examples/gno.land/r/gov/dao/types.gno index f56f45c0606..5f24cbf0c34 100644 --- a/examples/gno.land/r/gov/dao/types.gno +++ b/examples/gno.land/r/gov/dao/types.gno @@ -191,39 +191,34 @@ type DAO interface { // It is intended to be used to get the std.Address of the proposal, that // may vary depending on the DAO implementation, and to validate that // the requester is allowed to do a proposal - PreCreateProposal(ctx *Context, r ProposalRequest) (std.Address, error) + PreCreateProposal(r ProposalRequest) (std.Address, error) // PostCreateProposal is called after creating the Proposal. It is // intended to be used as a way to store a new proposal status, that // depends on the actuall govDAO implementation - PostCreateProposal(ctx *Context, r ProposalRequest, pid ProposalID) + PostCreateProposal(r ProposalRequest, pid ProposalID) // VoteOnProposal will send a petition to vote for a specific proposal // to the actual govDAO implementation - VoteOnProposal(ctx *Context, r VoteRequest) error + VoteOnProposal(r VoteRequest) error // PreGetProposal is called when someone is trying to get a proposal by ID. // Is intended to be used to validate who can query proposals, just in case // the actual govDAO implementation wants to limit the access. - PreGetProposal(ctx *Context, pid ProposalID) error + PreGetProposal(pid ProposalID) error // PostGetProposal is called after the proposal has been obtained. Intended to be // used by govDAO implementations if they need to check Proposal data to know if // the caller is allowed to get that kind of Proposal or not. - PostGetProposal(ctx *Context, pid ProposalID, p *Proposal) error + PostGetProposal(pid ProposalID, p *Proposal) error // PreExecuteProposal is called when someone is trying to execute a proposal by ID. // Is intended to be used to validate who can trigger the proposal execution. - PreExecuteProposal(ctx *Context, pid ProposalID) (bool, error) + PreExecuteProposal(pid ProposalID) (bool, error) // Render will return a human-readable string in markdown format that // will be used to show new data through the dao proxy entrypoint. - Render(ctx *Context, path string) string -} - -type Context struct { - PrevRealm std.Realm - CurrentRealm std.Realm + Render(pkgpath string, path string) string } type UpdateRequest struct { diff --git a/examples/gno.land/r/gov/dao/v3/impl/govdao.gno b/examples/gno.land/r/gov/dao/v3/impl/govdao.gno index aa2caa56c66..dfd2d8128d5 100644 --- a/examples/gno.land/r/gov/dao/v3/impl/govdao.gno +++ b/examples/gno.land/r/gov/dao/v3/impl/govdao.gno @@ -24,23 +24,49 @@ func NewGovDAO() *GovDAO { d.render = NewRender(d) - _govdao = d + // There was no realm, from main(), so it succeeded, And + // when returning, there was no finalization. We don't + // finalize anyways because there wasn't a realm boundary. + // XXX make filetest main package a realm. + // + // filetest.init() -> + // v3/init.Init() -> + // NewGovDAO() -> + // returns an unsaved DAO NOTE NO REALM! + // dao.UpdateImpl => + // saves dao under + // + // r/gov/dao.CrossPropposal() -> + // proposals.SetProposal(), + // that proposal lives in r/gov/dao. + // r/gov/dao.ExecuteProposal() -> + // g.PreExecuteProposal() -> + // XXX g.test = 1 fails, owned by gov/dao. + // + // + cross(func() { + crossing() + // TODO: replace with future attach() + _govdao = d + })() return d } -// Setting this to a global variable forces attaching the GovDAO struct to this realm. +// Setting this to a global variable forces attaching the GovDAO struct to this +// realm. TODO replace with future `attach()`. var _govdao *GovDAO -func (g *GovDAO) PreCreateProposal(ctx *dao.Context, r dao.ProposalRequest) (std.Address, error) { - if !g.isValidCall(ctx) { - return "", errors.New("proposal creation must be done directly by a user") +func (g *GovDAO) PreCreateProposal(r dao.ProposalRequest) (std.Address, error) { + if !g.isValidCall() { + return "", errors.New(ufmt.Sprintf("proposal creation must be done directly by a user or through the r/gov/dao proxy. current realm: %v; previous realm: %v", + std.CurrentRealm(), std.PreviousRealm())) } return std.OriginCaller(), nil } -func (g *GovDAO) PostCreateProposal(ctx *dao.Context, r dao.ProposalRequest, pid dao.ProposalID) { +func (g *GovDAO) PostCreateProposal(r dao.ProposalRequest, pid dao.ProposalID) { // Tiers Allowed to Vote tatv := []string{memberstore.T1, memberstore.T2, memberstore.T3} switch v := r.Filter().(type) { @@ -59,13 +85,13 @@ func (g *GovDAO) PostCreateProposal(ctx *dao.Context, r dao.ProposalRequest, pid g.pss.Set(pids, newProposalStatus(tatv)) } -func (g *GovDAO) VoteOnProposal(ctx *dao.Context, r dao.VoteRequest) error { - if !g.isValidCall(ctx) { +func (g *GovDAO) VoteOnProposal(r dao.VoteRequest) error { + if !g.isValidCall() { return errors.New("proposal voting must be done directly by a user") } caller := std.OriginCaller() - mem, tie := memberstore.Get().GetMember(caller) + mem, tie := cross(getMembers)().GetMember(caller) if mem == nil { return ErrMemberNotFound } @@ -99,16 +125,16 @@ func (g *GovDAO) VoteOnProposal(ctx *dao.Context, r dao.VoteRequest) error { return nil } -func (g *GovDAO) PreGetProposal(ctx *dao.Context, pid dao.ProposalID) error { +func (g *GovDAO) PreGetProposal(pid dao.ProposalID) error { return nil } -func (g *GovDAO) PostGetProposal(ctx *dao.Context, pid dao.ProposalID, p *dao.Proposal) error { +func (g *GovDAO) PostGetProposal(pid dao.ProposalID, p *dao.Proposal) error { return nil } -func (g *GovDAO) PreExecuteProposal(ctx *dao.Context, pid dao.ProposalID) (bool, error) { - if !g.isValidCall(ctx) { +func (g *GovDAO) PreExecuteProposal(pid dao.ProposalID) (bool, error) { + if !g.isValidCall() { return false, errors.New("proposal execution must be done directly by a user") } status := g.pss.GetStatus(pid) @@ -129,26 +155,24 @@ func (g *GovDAO) PreExecuteProposal(ctx *dao.Context, pid dao.ProposalID) (bool, return false, errors.New(ufmt.Sprintf("proposal didn't reach supermajority yet: %v", law.Supermajority)) } -func (g *GovDAO) Render(ctx *dao.Context, path string) string { - return g.render.Render(path, ctx.CurrentRealm.PkgPath()) +func (g *GovDAO) Render(pkgPath string, path string) string { + return g.render.Render(pkgPath, path) } -func (g *GovDAO) isValidCall(ctx *dao.Context) bool { +func (g *GovDAO) isValidCall() bool { // We need to verify two cases: - // 1: r/gov/dao (proxy) was called directly by an user - // 2: r/gov/dao/v3/impl was called directly by an user + // 1: r/gov/dao (proxy) functions called directly by an user + // 2: r/gov/dao/v3/impl methods called directly by an user // case 1 - // if ctx.CurrentRealm.PkgPath() == "gno.land/r/gov/dao" && ctx.PrevRealm.IsUser() { - // return true - // } + if std.CurrentRealm().PkgPath() == "gno.land/r/gov/dao" && std.PreviousRealm().IsUser() { + return true + } // case 2 - // if ctx.CurrentRealm.PkgPath() == "gno.land/r/gov/dao/v3/impl" && ctx.PrevRealm.IsUser() { - // return true - // } - - // return false + if std.CurrentRealm().IsUser() { + return true + } - return true // TODO: FIX: https://github.com/gnolang/gno/issues/4066 + return false } diff --git a/examples/gno.land/r/gov/dao/v3/impl/govdao_test.gno b/examples/gno.land/r/gov/dao/v3/impl/govdao_test.gno index e37cc5d3476..ad7512d1ef0 100644 --- a/examples/gno.land/r/gov/dao/v3/impl/govdao_test.gno +++ b/examples/gno.land/r/gov/dao/v3/impl/govdao_test.gno @@ -13,7 +13,7 @@ import ( func init() { loadMembers() - dao.UpdateImpl(dao.UpdateRequest{ + cross(dao.UpdateImpl)(dao.UpdateRequest{ DAO: govDAO, AllowedDAOs: []string{"gno.land/r/gov/dao/v3/impl"}, }) @@ -36,22 +36,23 @@ var ( func loadMembers() { // This is needed because state is saved between unit tests, // and we want to avoid having real members used on tests - memberstore.Get().DeleteAll() - - memberstore.Get().SetTier(memberstore.T1) - memberstore.Get().SetTier(memberstore.T2) - memberstore.Get().SetTier(memberstore.T3) - - memberstore.Get().SetMember(memberstore.T1, m1, memberByTier(memberstore.T1)) - memberstore.Get().SetMember(memberstore.T1, m11, memberByTier(memberstore.T1)) - memberstore.Get().SetMember(memberstore.T1, m111, memberByTier(memberstore.T1)) - memberstore.Get().SetMember(memberstore.T1, m1111, memberByTier(memberstore.T1)) - - memberstore.Get().SetMember(memberstore.T2, m2, memberByTier(memberstore.T2)) - memberstore.Get().SetMember(memberstore.T2, m3, memberByTier(memberstore.T2)) - memberstore.Get().SetMember(memberstore.T3, m4, memberByTier(memberstore.T3)) - memberstore.Get().SetMember(memberstore.T3, m5, memberByTier(memberstore.T3)) - memberstore.Get().SetMember(memberstore.T3, m6, memberByTier(memberstore.T3)) + mstore := memberstore.Get() + mstore.DeleteAll() + + mstore.SetTier(memberstore.T1) + mstore.SetTier(memberstore.T2) + mstore.SetTier(memberstore.T3) + + mstore.SetMember(memberstore.T1, m1, memberByTier(memberstore.T1)) + mstore.SetMember(memberstore.T1, m11, memberByTier(memberstore.T1)) + mstore.SetMember(memberstore.T1, m111, memberByTier(memberstore.T1)) + mstore.SetMember(memberstore.T1, m1111, memberByTier(memberstore.T1)) + + mstore.SetMember(memberstore.T2, m2, memberByTier(memberstore.T2)) + mstore.SetMember(memberstore.T2, m3, memberByTier(memberstore.T2)) + mstore.SetMember(memberstore.T3, m4, memberByTier(memberstore.T3)) + mstore.SetMember(memberstore.T3, m5, memberByTier(memberstore.T3)) + mstore.SetMember(memberstore.T3, m6, memberByTier(memberstore.T3)) } func TestCreateProposalAndVote(t *testing.T) { @@ -60,6 +61,7 @@ func TestCreateProposalAndVote(t *testing.T) { portfolio := "# This is my portfolio:\n\n- THINGS" testing.SetOriginCaller(noMember) + testing.SetRealm(std.NewCodeRealm("gno.land/r/gov/dao/v3/impl")) nm1 := testutils.TestAddress("nm1") @@ -72,7 +74,7 @@ func TestCreateProposalAndVote(t *testing.T) { }) testing.SetOriginCaller(m1) - testing.SetRealm(std.NewUserRealm(m1)) + testing.SetRealm(std.NewCodeRealm("gno.land/r/gov/dao/v3/impl")) pid := dao.MustCreateProposal(NewAddMemberRequest(nm1, memberstore.T2, portfolio)) urequire.Equal(t, int(pid), 0) @@ -84,6 +86,7 @@ func TestCreateProposalAndVote(t *testing.T) { }) testing.SetOriginCaller(m11) + testing.SetRealm(std.NewCodeRealm("gno.land/r/gov/dao/v3/impl")) dao.MustVoteOnProposal(dao.VoteRequest{ Option: dao.NoVote, @@ -91,6 +94,7 @@ func TestCreateProposalAndVote(t *testing.T) { }) testing.SetOriginCaller(m2) + testing.SetRealm(std.NewCodeRealm("gno.land/r/gov/dao/v3/impl")) dao.MustVoteOnProposal(dao.VoteRequest{ Option: dao.NoVote, @@ -98,6 +102,7 @@ func TestCreateProposalAndVote(t *testing.T) { }) testing.SetOriginCaller(m3) + testing.SetRealm(std.NewCodeRealm("gno.land/r/gov/dao/v3/impl")) dao.MustVoteOnProposal(dao.VoteRequest{ Option: dao.NoVote, @@ -105,6 +110,7 @@ func TestCreateProposalAndVote(t *testing.T) { }) testing.SetOriginCaller(m4) + testing.SetRealm(std.NewCodeRealm("gno.land/r/gov/dao/v3/impl")) urequire.PanicsWithMessage(t, "member on specified tier is not allowed to vote on this proposal", func() { dao.MustVoteOnProposal(dao.VoteRequest{ @@ -114,6 +120,7 @@ func TestCreateProposalAndVote(t *testing.T) { }) testing.SetOriginCaller(m111) + testing.SetRealm(std.NewCodeRealm("gno.land/r/gov/dao/v3/impl")) // Same effect as: // dao.MustVoteOnProposal(dao.VoteRequest{ @@ -131,6 +138,7 @@ func TestCreateProposalAndVote(t *testing.T) { }) testing.SetOriginCaller(m1111) + testing.SetRealm(std.NewCodeRealm("gno.land/r/gov/dao/v3/impl")) dao.MustVoteOnProposal(dao.VoteRequest{ Option: dao.NoVote, @@ -149,6 +157,7 @@ func TestProposalPagination(t *testing.T) { portfolio := "### This is my portfolio:\n\n- THINGS" testing.SetOriginCaller(m1) + testing.SetRealm(std.NewCodeRealm("gno.land/r/gov/dao/v3/impl")) nm1 := testutils.TestAddress("nm1") @@ -188,6 +197,7 @@ func TestUpgradeDaoImplementation(t *testing.T) { loadMembers() testing.SetOriginCaller(noMember) + testing.SetRealm(std.NewCodeRealm("gno.land/r/gov/dao/v3/impl")) nm1 := testutils.TestAddress("nm1") @@ -197,7 +207,7 @@ func TestUpgradeDaoImplementation(t *testing.T) { }) testing.SetOriginCaller(m1) - testing.SetRealm(std.NewUserRealm(m1)) + testing.SetRealm(std.NewCodeRealm("gno.land/r/gov/dao/v3/impl")) preq := NewUpgradeDaoImplRequest(govDAO, "gno.land/r/gov/dao/v4/impl", "Something happened and we have to fix it.") pid := dao.MustCreateProposal(preq) @@ -210,6 +220,7 @@ func TestUpgradeDaoImplementation(t *testing.T) { }) testing.SetOriginCaller(m11) + testing.SetRealm(std.NewCodeRealm("gno.land/r/gov/dao/v3/impl")) dao.MustVoteOnProposal(dao.VoteRequest{ Option: dao.YesVote, @@ -217,6 +228,7 @@ func TestUpgradeDaoImplementation(t *testing.T) { }) testing.SetOriginCaller(m2) + testing.SetRealm(std.NewCodeRealm("gno.land/r/gov/dao/v3/impl")) dao.MustVoteOnProposal(dao.VoteRequest{ Option: dao.YesVote, @@ -224,15 +236,15 @@ func TestUpgradeDaoImplementation(t *testing.T) { }) testing.SetOriginCaller(m3) + testing.SetRealm(std.NewCodeRealm("gno.land/r/gov/dao/v3/impl")) dao.MustVoteOnProposal(dao.VoteRequest{ Option: dao.YesVote, ProposalID: dao.ProposalID(pid), }) - testing.SetOriginCaller(m4) - testing.SetOriginCaller(m111) + testing.SetRealm(std.NewCodeRealm("gno.land/r/gov/dao/v3/impl")) // Same effect as: // dao.MustVoteOnProposal(dao.VoteRequest{ diff --git a/examples/gno.land/r/gov/dao/v3/impl/impl.gno b/examples/gno.land/r/gov/dao/v3/impl/impl.gno index 49441cd8e58..409dd1a144a 100644 --- a/examples/gno.land/r/gov/dao/v3/impl/impl.gno +++ b/examples/gno.land/r/gov/dao/v3/impl/impl.gno @@ -3,12 +3,12 @@ package impl import ( "std" - "gno.land/r/gov/dao" "gno.land/r/gov/dao/v3/memberstore" ) var govDAO *GovDAO = NewGovDAO() var law *Law +var realm = std.CurrentRealm() func init() { law = &Law{ @@ -16,15 +16,8 @@ func init() { } } -func context() *dao.Context { - return &dao.Context{ - PrevRealm: std.PreviousRealm(), - CurrentRealm: std.CurrentRealm(), - } -} - func Render(in string) string { - return govDAO.Render(context(), in) + return govDAO.Render(realm.PkgPath(), in) } // AddMember allows T1 and T2 members to freely add T3 members using their invitation points. diff --git a/examples/gno.land/r/gov/dao/v3/impl/render.gno b/examples/gno.land/r/gov/dao/v3/impl/render.gno index d80bb81f6d2..13b98d915e3 100644 --- a/examples/gno.land/r/gov/dao/v3/impl/render.gno +++ b/examples/gno.land/r/gov/dao/v3/impl/render.gno @@ -45,12 +45,13 @@ func NewRender(d *GovDAO) *render { return ren } -func (ren *render) Render(path string, pkg string) string { - relativePath, found := strings.CutPrefix(pkg, std.ChainDomain()) +func (ren *render) Render(pkgPath string, path string) string { + relativePath, found := strings.CutPrefix(pkgPath, std.ChainDomain()) if !found { - panic(ufmt.Sprintf("realm package with unexpected name found: %v in chain domain %v", pkg, std.ChainDomain())) + panic(ufmt.Sprintf( + "realm package with unexpected name found: %v in chain domain %v", + pkgPath, std.ChainDomain())) } - ren.relativeRealmPath = relativePath return ren.router.Render(path) } @@ -80,7 +81,7 @@ func (ren *render) renderProposal(sPid string, d *GovDAO) string { } ps := d.pss.GetStatus(dao.ProposalID(pid)) - p := dao.MustGetProposal(dao.ProposalID(pid)) + p := cross(dao.MustGetProposal)(dao.ProposalID(pid)) out := "" out += ufmt.Sprintf("## Proposal with id: %v", sPid) diff --git a/examples/gno.land/r/gov/dao/v3/impl/types.gno b/examples/gno.land/r/gov/dao/v3/impl/types.gno index 1d1408efc18..a6a38965496 100644 --- a/examples/gno.land/r/gov/dao/v3/impl/types.gno +++ b/examples/gno.land/r/gov/dao/v3/impl/types.gno @@ -56,6 +56,11 @@ type proposalStatus struct { TotalPower float64 // TotalPower is the power of all the members existing when this proposal was created. } +func getMembers() memberstore.MembersByTier { + crossing() + return memberstore.Get() +} + func newProposalStatus(allowedToVote []string) *proposalStatus { yv := memberstore.NewMembersByTier() yv.SetTier(memberstore.T1) @@ -77,7 +82,7 @@ func newProposalStatus(allowedToVote []string) *proposalStatus { TiersAllowedToVote: allowedToVote, - TotalPower: memberstore.Get().GetTotalPower(), + TotalPower: cross(getMembers)().GetTotalPower(), } } @@ -90,7 +95,7 @@ func (ps *proposalStatus) YesPercent() float64 { panic("type must be memberstore.Tier") } - power := tier.PowerHandler(memberstore.Get(), memberstore.Tiers) + power := tier.PowerHandler(cross(getMembers)(), memberstore.Tiers) ts := ps.YesVotes.GetTierSize(tn) yp = yp + (power * float64(ts)) @@ -110,7 +115,7 @@ func (ps *proposalStatus) NoPercent() float64 { panic("type must be memberstore.Tier") } - power := tier.PowerHandler(memberstore.Get(), memberstore.Tiers) + power := tier.PowerHandler(cross(getMembers)(), memberstore.Tiers) ts := ps.NoVotes.GetTierSize(tn) np = np + (power * float64(ts)) @@ -182,7 +187,7 @@ func writeVotes(sb *strings.Builder, t memberstore.MembersByTier, title string) panic("tier not found") } - power := tier.PowerHandler(memberstore.Get(), memberstore.Tiers) + power := tier.PowerHandler(cross(getMembers)(), memberstore.Tiers) sb.WriteString(ufmt.Sprintf("### %v from %v (VPPM %v):\n", title, tn, power)) ms, _ := value.(*avl.Tree) diff --git a/examples/gno.land/r/gov/dao/v3/init/init.gno b/examples/gno.land/r/gov/dao/v3/init/init.gno index b51ded753ab..dca06497536 100644 --- a/examples/gno.land/r/gov/dao/v3/init/init.gno +++ b/examples/gno.land/r/gov/dao/v3/init/init.gno @@ -12,7 +12,7 @@ func Init() { // This is needed because state is saved between unit tests, // and we want to avoid having real members used on tests memberstore.Get().DeleteAll() - dao.UpdateImpl(dao.UpdateRequest{ + cross(dao.UpdateImpl)(dao.UpdateRequest{ DAO: impl.NewGovDAO(), AllowedDAOs: []string{"gno.land/r/gov/dao/v3/impl"}, }) @@ -24,10 +24,13 @@ func InitWithUsers(addrs ...std.Address) { memberstore.Get().DeleteAll() memberstore.Get().SetTier(memberstore.T1) for _, a := range addrs { + if !a.IsValid() { + panic("invalid address: " + a.String()) + } memberstore.Get().SetMember(memberstore.T1, a, &memberstore.Member{InvitationPoints: 3}) } - dao.UpdateImpl(dao.UpdateRequest{ + cross(dao.UpdateImpl)(dao.UpdateRequest{ DAO: impl.NewGovDAO(), AllowedDAOs: []string{"gno.land/r/gov/dao/v3/impl"}, }) diff --git a/examples/gno.land/r/gov/dao/v3/loader/loader.gno b/examples/gno.land/r/gov/dao/v3/loader/loader.gno index 7e65d7aaf12..536bf14f6ff 100644 --- a/examples/gno.land/r/gov/dao/v3/loader/loader.gno +++ b/examples/gno.land/r/gov/dao/v3/loader/loader.gno @@ -1,12 +1,52 @@ package loader import ( + "std" + "gno.land/r/gov/dao" "gno.land/r/gov/dao/v3/impl" + "gno.land/r/gov/dao/v3/memberstore" ) // this is only executed when loaded into genesis func init() { + memberstore.Get().SetTier(memberstore.T1) + memberstore.Get().SetMember(memberstore.T1, std.Address("g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj"), &memberstore.Member{InvitationPoints: 3}) // Jae + memberstore.Get().SetMember(memberstore.T1, std.Address("g1manfred47kzduec920z88wfr64ylksmdcedlf5"), &memberstore.Member{InvitationPoints: 3}) // Manfred + memberstore.Get().SetMember(memberstore.T1, std.Address("g1e6gxg5tvc55mwsn7t7dymmlasratv7mkv0rap2"), &memberstore.Member{InvitationPoints: 3}) // Milos + memberstore.Get().SetMember(memberstore.T1, std.Address("g1qhskthp2uycmg4zsdc9squ2jds7yv3t0qyrlnp"), &memberstore.Member{InvitationPoints: 3}) // Petar + memberstore.Get().SetMember(memberstore.T1, std.Address("g18amm3fc00t43dcxsys6udug0czyvqt9e7p23rd"), &memberstore.Member{InvitationPoints: 3}) // Marc + memberstore.Get().SetMember(memberstore.T1, std.Address("g19p3yzr3cuhzqa02j0ce6kzvyjqfzwemw3vam0x"), &memberstore.Member{InvitationPoints: 3}) // Guilhem + memberstore.Get().SetMember(memberstore.T1, std.Address("g1mx4pum9976th863jgry4sdjzfwu03qan5w2v9j"), &memberstore.Member{InvitationPoints: 3}) // Ray + memberstore.Get().SetMember(memberstore.T1, std.Address("g127l4gkhk0emwsx5tmxe96sp86c05h8vg5tufzq"), &memberstore.Member{InvitationPoints: 3}) // Maxwell + memberstore.Get().SetMember(memberstore.T1, std.Address("g1m0rgan0rla00ygmdmp55f5m0unvsvknluyg2a4"), &memberstore.Member{InvitationPoints: 3}) // Morgan + memberstore.Get().SetMember(memberstore.T1, std.Address("g1ker4vvggvsyatexxn3hkthp2hu80pkhrwmuczr"), &memberstore.Member{InvitationPoints: 3}) // Sergio + memberstore.Get().SetMember(memberstore.T1, std.Address("g18x425qmujg99cfz3q97y4uep5pxjq3z8lmpt25"), &memberstore.Member{InvitationPoints: 3}) // Antoine + memberstore.Get().SetMember(memberstore.T1, std.Address("g16tfrrul20g4jzt3z303raqw8vs8s2pqqh5clwu"), &memberstore.Member{InvitationPoints: 3}) // Ilker + memberstore.Get().SetMember(memberstore.T1, std.Address("g1hy6zry03hg5d8le9s2w4fxme6236hkgd928dun"), &memberstore.Member{InvitationPoints: 3}) // Jerónimo + memberstore.Get().SetMember(memberstore.T1, std.Address("g15ruzptpql4dpuyzej0wkt5rq6r26kw4nxu9fwd"), &memberstore.Member{InvitationPoints: 3}) // Denis + memberstore.Get().SetMember(memberstore.T1, std.Address("g1lckl8j2g3jyyuq6fx7pke3uz4kemht7lw4fg5l"), &memberstore.Member{InvitationPoints: 3}) // Danny + memberstore.Get().SetMember(memberstore.T1, std.Address("g1778y2yphxs2wpuaflsy5y9qwcd4gttn4g5yjx5"), &memberstore.Member{InvitationPoints: 3}) // Michelle + memberstore.Get().SetMember(memberstore.T1, std.Address("g1mq7g0jszdmn4qdpc9tq94w0gyex37su892n80m"), &memberstore.Member{InvitationPoints: 3}) // Alan + memberstore.Get().SetMember(memberstore.T1, std.Address("g197q5e9v00vuz256ly7fq7v3ekaun5cr7wmjgfh"), &memberstore.Member{InvitationPoints: 3}) // Salvo + memberstore.Get().SetMember(memberstore.T1, std.Address("g1mpkp5lm8lwpm0pym4388836d009zfe4maxlqsq"), &memberstore.Member{InvitationPoints: 3}) // Alexis + memberstore.Get().SetMember(memberstore.T1, std.Address("g125em6arxsnj49vx35f0n0z34putv5ty3376fg5"), &memberstore.Member{InvitationPoints: 3}) // Leon + memberstore.Get().SetMember(memberstore.T1, std.Address("g1whzkakk4hzjkvy60d5pwfk484xu67ar2cl62h2"), &memberstore.Member{InvitationPoints: 3}) // Kirk + memberstore.Get().SetMember(memberstore.T1, std.Address("g1sw5xklxjjuv0yvuxy5f5s3l3mnj0nqq626a9wr"), &memberstore.Member{InvitationPoints: 3}) // Albert + + memberstore.Get().SetTier(memberstore.T2) + memberstore.Get().SetMember(memberstore.T2, std.Address("g1jazghxvvgz3egnr2fc8uf72z4g0l03596y9ls7"), &memberstore.Member{InvitationPoints: 2}) // Nemanja + memberstore.Get().SetMember(memberstore.T2, std.Address("g1dfr24yhk5ztwtqn2a36m8f6ud8cx5hww4dkjfl"), &memberstore.Member{InvitationPoints: 2}) // Antonio + memberstore.Get().SetMember(memberstore.T2, std.Address("g12vx7dn3dqq89mz550zwunvg4qw6epq73d9csay"), &memberstore.Member{InvitationPoints: 2}) // Dongwon + memberstore.Get().SetMember(memberstore.T2, std.Address("g1r04aw56fgvzy859fachr8hzzhqkulkaemltr76"), &memberstore.Member{InvitationPoints: 2}) // Blake + memberstore.Get().SetMember(memberstore.T2, std.Address("g17n4y745s08awwq4e0a38lagsgtntna0749tnxe"), &memberstore.Member{InvitationPoints: 2}) // Jinwoo + memberstore.Get().SetMember(memberstore.T2, std.Address("g1ckae7tc5sez8ul3ssne75sk4muwgttp6ks2ky9"), &memberstore.Member{InvitationPoints: 2}) // ByeongJun + + memberstore.Get().SetTier(memberstore.T3) + memberstore.Get().SetMember(memberstore.T3, std.Address("g14u5eaheavy0ux4dmpykg2gvxpvqvexm9cyg58a"), &memberstore.Member{InvitationPoints: 1}) // Norman + memberstore.Get().SetMember(memberstore.T3, std.Address("g1qynsu9dwj9lq0m5fkje7jh6qy3md80ztqnshhm"), &memberstore.Member{InvitationPoints: 1}) // Rémi + memberstore.Get().SetMember(memberstore.T3, std.Address("g17ernafy6ctpcz6uepfsq2js8x2vz0wladh5yc3"), &memberstore.Member{InvitationPoints: 1}) // Dragos + cross(dao.UpdateImpl)(dao.UpdateRequest{ DAO: impl.GetInstance(), AllowedDAOs: []string{"gno.land/r/gov/dao/v3/impl"}, diff --git a/examples/gno.land/r/gov/dao/v3/memberstore/memberstore.gno b/examples/gno.land/r/gov/dao/v3/memberstore/memberstore.gno index e39102dd8b0..e8f68f835c2 100644 --- a/examples/gno.land/r/gov/dao/v3/memberstore/memberstore.gno +++ b/examples/gno.land/r/gov/dao/v3/memberstore/memberstore.gno @@ -92,43 +92,6 @@ func init() { }, }) - members.SetTier(T1) - members.SetMember(T1, std.Address("g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj"), &Member{InvitationPoints: 3}) // Jae - members.SetMember(T1, std.Address("g1manfred47kzduec920z88wfr64ylksmdcedlf5"), &Member{InvitationPoints: 3}) // Manfred - members.SetMember(T1, std.Address("g1e6gxg5tvc55mwsn7t7dymmlasratv7mkv0rap2"), &Member{InvitationPoints: 3}) // Milos - members.SetMember(T1, std.Address("g1qhskthp2uycmg4zsdc9squ2jds7yv3t0qyrlnp"), &Member{InvitationPoints: 3}) // Petar - members.SetMember(T1, std.Address("g18amm3fc00t43dcxsys6udug0czyvqt9e7p23rd"), &Member{InvitationPoints: 3}) // Marc - members.SetMember(T1, std.Address("g19p3yzr3cuhzqa02j0ce6kzvyjqfzwemw3vam0x"), &Member{InvitationPoints: 3}) // Guilhem - members.SetMember(T1, std.Address("g1mx4pum9976th863jgry4sdjzfwu03qan5w2v9j"), &Member{InvitationPoints: 3}) // Ray - members.SetMember(T1, std.Address("g127l4gkhk0emwsx5tmxe96sp86c05h8vg5tufzq"), &Member{InvitationPoints: 3}) // Maxwell - members.SetMember(T1, std.Address("g1m0rgan0rla00ygmdmp55f5m0unvsvknluyg2a4"), &Member{InvitationPoints: 3}) // Morgan - members.SetMember(T1, std.Address("g1ker4vvggvsyatexxn3hkthp2hu80pkhrwmuczr"), &Member{InvitationPoints: 3}) // Sergio - members.SetMember(T1, std.Address("g18x425qmujg99cfz3q97y4uep5pxjq3z8lmpt25"), &Member{InvitationPoints: 3}) // Antoine - members.SetMember(T1, std.Address("g16tfrrul20g4jzt3z303raqw8vs8s2pqqh5clwu"), &Member{InvitationPoints: 3}) // Ilker - members.SetMember(T1, std.Address("g1hy6zry03hg5d8le9s2w4fxme6236hkgd928dun"), &Member{InvitationPoints: 3}) // Jerónimo - members.SetMember(T1, std.Address("g15ruzptpql4dpuyzej0wkt5rq6r26kw4nxu9fwd"), &Member{InvitationPoints: 3}) // Denis - members.SetMember(T1, std.Address("g1lckl8j2g3jyyuq6fx7pke3uz4kemht7lw4fg5l"), &Member{InvitationPoints: 3}) // Danny - members.SetMember(T1, std.Address("g1778y2yphxs2wpuaflsy5y9qwcd4gttn4g5yjx5"), &Member{InvitationPoints: 3}) // Michelle - members.SetMember(T1, std.Address("g1mq7g0jszdmn4qdpc9tq94w0gyex37su892n80m"), &Member{InvitationPoints: 3}) // Alan - members.SetMember(T1, std.Address("g197q5e9v00vuz256ly7fq7v3ekaun5cr7wmjgfh"), &Member{InvitationPoints: 3}) // Salvo - members.SetMember(T1, std.Address("g1mpkp5lm8lwpm0pym4388836d009zfe4maxlqsq"), &Member{InvitationPoints: 3}) // Alexis - members.SetMember(T1, std.Address("g125em6arxsnj49vx35f0n0z34putv5ty3376fg5"), &Member{InvitationPoints: 3}) // Leon - members.SetMember(T1, std.Address("g1whzkakk4hzjkvy60d5pwfk484xu67ar2cl62h2"), &Member{InvitationPoints: 3}) // Kirk - members.SetMember(T1, std.Address("g1sw5xklxjjuv0yvuxy5f5s3l3mnj0nqq626a9wr"), &Member{InvitationPoints: 3}) // Albert - - members.SetTier(T2) - members.SetMember(T2, std.Address("g1jazghxvvgz3egnr2fc8uf72z4g0l03596y9ls7"), &Member{InvitationPoints: 2}) // Nemanja - members.SetMember(T2, std.Address("g1dfr24yhk5ztwtqn2a36m8f6ud8cx5hww4dkjfl"), &Member{InvitationPoints: 2}) // Antonio - members.SetMember(T2, std.Address("g12vx7dn3dqq89mz550zwunvg4qw6epq73d9csay"), &Member{InvitationPoints: 2}) // Dongwon - members.SetMember(T2, std.Address("g1r04aw56fgvzy859fachr8hzzhqkulkaemltr76"), &Member{InvitationPoints: 2}) // Blake - members.SetMember(T2, std.Address("g17n4y745s08awwq4e0a38lagsgtntna0749tnxe"), &Member{InvitationPoints: 2}) // Jinwoo - members.SetMember(T2, std.Address("g1ckae7tc5sez8ul3ssne75sk4muwgttp6ks2ky9"), &Member{InvitationPoints: 2}) // ByeongJun - - members.SetTier(T3) - members.SetMember(T3, std.Address("g14u5eaheavy0ux4dmpykg2gvxpvqvexm9cyg58a"), &Member{InvitationPoints: 1}) // Norman - members.SetMember(T3, std.Address("g1qynsu9dwj9lq0m5fkje7jh6qy3md80ztqnshhm"), &Member{InvitationPoints: 1}) // Rémi - members.SetMember(T3, std.Address("g17ernafy6ctpcz6uepfsq2js8x2vz0wladh5yc3"), &Member{InvitationPoints: 1}) // Dragos - } func Render(string) string { @@ -159,7 +122,7 @@ func Render(string) string { // Get gets the Members store func Get() MembersByTier { - realm := std.PreviousRealm().PkgPath() + realm := std.CurrentRealm().PkgPath() if !dao.InAllowedDAOs(realm) { panic("this Realm is not allowed to get the Members data: " + realm) } diff --git a/examples/gno.land/r/leon/hor/hof_test.gno b/examples/gno.land/r/leon/hor/hof_test.gno index f55f8b00408..fec966802b4 100644 --- a/examples/gno.land/r/leon/hor/hof_test.gno +++ b/examples/gno.land/r/leon/hor/hof_test.gno @@ -325,3 +325,12 @@ func TestSortByCreation(t *testing.T) { uassert.Equal(t, secondValue.pkgpath, rlmPath4) } + +func TestGetScore(t *testing.T) { + score, err := h.GetScore(42) + uassert.Nil(t, err) + uassert.Equal(t, 0, score) + uassert.NotPanics(t, func() { + uassert.Equal(t, 0, h.MustGetScore(42)) + }) +} diff --git a/examples/gno.land/r/morgan/chess/chess.gno b/examples/gno.land/r/morgan/chess/chess.gno index fa9ab6bbe2e..79c8793955b 100644 --- a/examples/gno.land/r/morgan/chess/chess.gno +++ b/examples/gno.land/r/morgan/chess/chess.gno @@ -126,6 +126,7 @@ func (g GameState) IsFinished() bool { // parallel games OR by introducing a "request" system, so that a game is not // immediately considered "open" when calling NewGame. func xNewGame(opponentRaw string, seconds, increment int) string { + crossing() assertOriginCall() if seconds >= 0 && increment < 0 { @@ -251,6 +252,7 @@ func getGame(id string, wantOpen bool) *Game { // must be specified. // Castling is specified by indicating the king's movement. func MakeMove(gameID, from, to string, promote chess.Piece) string { + crossing() assertOriginCall() g := getGame(gameID, true) @@ -379,6 +381,7 @@ func (g *Game) claimTimeout() error { // ClaimTimeout should be called when the caller believes the game has resulted // in a timeout. func ClaimTimeout(gameID string) string { + crossing() g := getGame(gameID, true) err := g.claimTimeout() @@ -388,6 +391,7 @@ func ClaimTimeout(gameID string) string { } func Abort(gameID string) string { + crossing() assertOriginCall() g := getGame(gameID, true) @@ -416,6 +420,7 @@ func abort(g *Game) error { } func Resign(gameID string) string { + crossing() assertOriginCall() g := getGame(gameID, true) @@ -450,6 +455,7 @@ func resign(g *Game) error { // DrawOffer creates a draw offer in the current game, if one doesn't already // exist. func DrawOffer(gameID string) string { + crossing() assertOriginCall() g := getGame(gameID, true) @@ -468,6 +474,7 @@ func DrawOffer(gameID string) string { // DrawRefuse refuse a draw offer in the given game. func DrawRefuse(gameID string) string { + crossing() assertOriginCall() g := getGame(gameID, true) @@ -492,6 +499,7 @@ func DrawRefuse(gameID string) string { // - Insufficient material (§9.4) // Note: stalemate happens as a consequence of a Move, and thus is handled in that function. func Draw(gameID string) string { + crossing() assertOriginCall() g := getGame(gameID, true) diff --git a/examples/gno.land/r/morgan/chess/chess_test.gno b/examples/gno.land/r/morgan/chess/chess_test.gno index 8d6d48dbc49..a6b7669ce48 100644 --- a/examples/gno.land/r/morgan/chess/chess_test.gno +++ b/examples/gno.land/r/morgan/chess/chess_test.gno @@ -265,7 +265,7 @@ func newTestCommandColorID(fn func(string) string, s string, addr string) testCo func (tc *testCommandColorID) Run(t *testing.T, bufs map[string]string) { testing.SetRealm(std.NewUserRealm(tc.addr)) - bufs["result"] = tc.fn(bufs["id"]) + bufs["result"] = cross(tc.fn)(bufs["id"]) } type testCommandNewGame struct { @@ -275,7 +275,7 @@ type testCommandNewGame struct { func (tc *testCommandNewGame) Run(t *testing.T, bufs map[string]string) { testing.SetRealm(std.NewUserRealm(tc.w)) - res := xNewGame(string(tc.b), tc.seconds, tc.incr) + res := cross(xNewGame)(string(tc.b), tc.seconds, tc.incr) bufs["result"] = res const idMagicString = `"id":"` @@ -296,7 +296,7 @@ type testCommandMove struct { func (tc *testCommandMove) Run(t *testing.T, bufs map[string]string) { testing.SetRealm(std.NewUserRealm(tc.addr)) - bufs["result"] = MakeMove(bufs["id"], tc.from, tc.to, tc.promotion) + bufs["result"] = cross(MakeMove)(bufs["id"], tc.from, tc.to, tc.promotion) } type testCommandGame struct { @@ -598,15 +598,12 @@ func runCommandTest(t *testing.T, command string) { } func catchPanic(tc testCommandRunner, t *testing.T, bufs map[string]string) { - defer func() { - // XXX: should prefer testing.Recover, but see: https://github.com/gnolang/gno/issues/1650 - e := recover() - if e == nil { - bufs["panic"] = "" - return - } - bufs["result"] = "" - bufs["panic"] = fmt.Sprint(e) - }() - tc.Run(t, bufs) + // XXX: should prefer testing.Recover, but see: https://github.com/gnolang/gno/issues/1650 + e := revive(func() { tc.Run(t, bufs) }) + if e == nil { + bufs["panic"] = "" + return + } + bufs["result"] = "" + bufs["panic"] = fmt.Sprint(e) } diff --git a/examples/gno.land/r/morgan/chess/lobby.gno b/examples/gno.land/r/morgan/chess/lobby.gno index 56f62707738..7c9c8e29c64 100644 --- a/examples/gno.land/r/morgan/chess/lobby.gno +++ b/examples/gno.land/r/morgan/chess/lobby.gno @@ -62,6 +62,7 @@ var ( ) func LobbyJoin(seconds, increment int) { + crossing() assertOriginCall() var tc tcLobby @@ -218,6 +219,7 @@ var bracketSize = [...]float64{ } func LobbyGameFound() string { + crossing() refreshLobby(tcLobby5p0) refreshLobby(tcLobby10p5) @@ -229,6 +231,7 @@ func LobbyGameFound() string { } func LobbyQuit() { + crossing() caller := std.PreviousRealm().Address() for tc, sublob := range lobby { for i, pl := range sublob { diff --git a/examples/gno.land/r/morgan/chess/lobby_test.gno b/examples/gno.land/r/morgan/chess/lobby_test.gno index 299725d40a9..4efadcab02d 100644 --- a/examples/gno.land/r/morgan/chess/lobby_test.gno +++ b/examples/gno.land/r/morgan/chess/lobby_test.gno @@ -12,11 +12,11 @@ import ( func TestLobbyJoin(t *testing.T) { cleanup() testing.SetRealm(std.NewUserRealm(white)) - LobbyJoin(10*60, 5) + cross(LobbyJoin)(10*60, 5) os.Sleep(time.Second * 5) testing.SetRealm(std.NewUserRealm(black)) - LobbyJoin(10*60, 5) - res := LobbyGameFound() + cross(LobbyJoin)(10*60, 5) + res := cross(LobbyGameFound)() if res == "null" { t.Errorf("LobbyGameFound is null") } @@ -153,7 +153,7 @@ func TestLobbyGameFound(t *testing.T) { } testing.SetRealm(std.NewUserRealm(tc.caller)) - game := LobbyGameFound() + game := cross(LobbyGameFound)() if tc.check != nil { tc.check(t) @@ -175,7 +175,7 @@ func TestLobbyJoin_HasOpenGames(t *testing.T) { addToUser2Games(black, g) testing.SetRealm(std.NewUserRealm(white)) - LobbyJoin(10*60, 5) + cross(LobbyJoin)(10*60, 5) if g.State != GameStateAborted { t.Errorf("state wrong: want %d got %d", GameStateAborted, g.State) } diff --git a/examples/gno.land/r/morgan/guestbook/admin.gno b/examples/gno.land/r/morgan/guestbook/admin.gno index 907585c63a7..e8c986d87fc 100644 --- a/examples/gno.land/r/morgan/guestbook/admin.gno +++ b/examples/gno.land/r/morgan/guestbook/admin.gno @@ -11,6 +11,7 @@ var owner = ownable.NewWithOrigin() // The user will still be marked as having submitted a message, so they // won't be able to re-submit a new message. func AdminDelete(signatureID string) { + crossing() owner.AssertOwnedByPrevious() id, err := seqid.FromString(signatureID) diff --git a/examples/gno.land/r/morgan/guestbook/guestbook.gno b/examples/gno.land/r/morgan/guestbook/guestbook.gno index 1a17d8df6b4..9f0d0bc87a7 100644 --- a/examples/gno.land/r/morgan/guestbook/guestbook.gno +++ b/examples/gno.land/r/morgan/guestbook/guestbook.gno @@ -43,6 +43,7 @@ const ( // Sign signs the guestbook, with the specified message. func Sign(message string) { + crossing() prev := std.PreviousRealm() switch { case !prev.IsUser(): diff --git a/examples/gno.land/r/morgan/guestbook/guestbook_test.gno b/examples/gno.land/r/morgan/guestbook/guestbook_test.gno index 1fe6b624af4..7344398c11a 100644 --- a/examples/gno.land/r/morgan/guestbook/guestbook_test.gno +++ b/examples/gno.land/r/morgan/guestbook/guestbook_test.gno @@ -14,10 +14,10 @@ func TestSign(t *testing.T) { hasSigned = avl.Tree{} testing.SetRealm(std.NewUserRealm("g1user")) - Sign("Hello!") + cross(Sign)("Hello!") testing.SetRealm(std.NewUserRealm("g1user2")) - Sign("Hello2!") + cross(Sign)("Hello2!") res := Render("") t.Log(res) @@ -35,19 +35,16 @@ func TestSign(t *testing.T) { func TestSign_FromRealm(t *testing.T) { testing.SetRealm(std.NewCodeRealm("gno.land/r/gnoland/users/v1")) - defer func() { - rec := recover() - if rec == nil { - t.Fatal("expected panic") - } - recString, ok := rec.(string) - if !ok { - t.Fatal("not a string", rec) - } else if recString != errNotAUser { - t.Fatal("invalid error", recString) - } - }() - Sign("Hey!") + rec := revive(func() { cross(Sign)("Hey!") }) + if rec == nil { + t.Fatal("expected panic") + } + recString, ok := rec.(string) + if !ok { + t.Fatal("not a string", rec) + } else if recString != errNotAUser { + t.Fatal("invalid error", recString) + } } func TestSign_Double(t *testing.T) { @@ -56,22 +53,18 @@ func TestSign_Double(t *testing.T) { hasSigned = avl.Tree{} testing.SetRealm(std.NewUserRealm("g1user")) - Sign("Hello!") - - defer func() { - rec := recover() - if rec == nil { - t.Fatal("expected panic") - } - recString, ok := rec.(string) - if !ok { - t.Error("type assertion failed", rec) - } else if recString != errAlreadySigned { - t.Error("invalid error message", recString) - } - }() - - Sign("Hello again!") + cross(Sign)("Hello!") + + rec := revive(func() { cross(Sign)("Hello again!") }) + if rec == nil { + t.Fatal("expected panic") + } + recString, ok := rec.(string) + if !ok { + t.Error("type assertion failed", rec) + } else if recString != errAlreadySigned { + t.Error("invalid error message", recString) + } } func TestSign_InvalidMessage(t *testing.T) { @@ -81,19 +74,16 @@ func TestSign_InvalidMessage(t *testing.T) { testing.SetRealm(std.NewUserRealm("g1user")) - defer func() { - rec := recover() - if rec == nil { - t.Fatal("expected panic") - } - recString, ok := rec.(string) - if !ok { - t.Error("type assertion failed", rec) - } else if recString != errInvalidCharacterInMessage { - t.Error("invalid error message", recString) - } - }() - Sign("\x00Hello!") + rec := revive(func() { cross(Sign)("\x00Hello!") }) + if rec == nil { + t.Fatal("expected panic") + } + recString, ok := rec.(string) + if !ok { + t.Error("type assertion failed", rec) + } else if recString != errInvalidCharacterInMessage { + t.Error("invalid error message", recString) + } } func TestAdminDelete(t *testing.T) { @@ -110,14 +100,14 @@ func TestAdminDelete(t *testing.T) { testing.SetRealm(std.NewUserRealm(userAddr)) const bad = "Very Bad Message! Nyeh heh heh!" - Sign(bad) + cross(Sign)(bad) if rnd := Render(""); !strings.Contains(rnd, bad) { t.Fatal("render does not contain bad message", rnd) } testing.SetRealm(std.NewUserRealm(adminAddr)) - AdminDelete(signatureID.String()) + cross(AdminDelete)(signatureID.String()) if rnd := Render(""); strings.Contains(rnd, bad) { t.Error("render contains bad message", rnd) diff --git a/examples/gno.land/r/moul/config/config.gno b/examples/gno.land/r/moul/config/config.gno index 0302163c3c8..2185c0eed6f 100644 --- a/examples/gno.land/r/moul/config/config.gno +++ b/examples/gno.land/r/moul/config/config.gno @@ -1,20 +1,76 @@ package config -import "std" +import ( + "errors" + "std" -var addr = std.Address("g1manfred47kzduec920z88wfr64ylksmdcedlf5") // @moul + "gno.land/p/moul/authz" +) -func Addr() std.Address { - return addr +// XXX: use NewWithOrigin +var Authorizer = authz.NewWithOrigin() + +// AddManager adds a new address to the list of authorized managers. +// This only works if the current authority is a MemberAuthority. +// The caller must be authorized by the current authority. +func AddManager(addr std.Address) error { + memberAuth, ok := Authorizer.Current().(*authz.MemberAuthority) + if !ok { + return errors.New("current authority is not a MemberAuthority, cannot add manager directly") + } + // Use the MemberAuthority's specific AddMember method, + // which internally performs the authorization check. + return memberAuth.AddMember(addr) +} + +// RemoveManager removes an address from the list of authorized managers. +// This only works if the current authority is a MemberAuthority. +// The caller must be authorized by the current authority. +func RemoveManager(addr std.Address) error { + memberAuth, ok := Authorizer.Current().(*authz.MemberAuthority) + if !ok { + return errors.New("current authority is not a MemberAuthority, cannot remove manager directly") + } + // Use the MemberAuthority's specific RemoveMember method, + // which internally performs the authorization check. + return memberAuth.RemoveMember(addr) } -func UpdateAddr(newAddr std.Address) { - AssertIsAdmin() - addr = newAddr +// TransferManagement transfers the authority to manage keys to a new authority. +// The caller must be authorized by the current authority. +func TransferManagement(newAuthority authz.Authority) error { + if newAuthority == nil { + return errors.New("new authority cannot be nil") + } + // Use the Authorizer's Transfer method, which handles the authorization check. + return Authorizer.Transfer(newAuthority) +} + +// ListManagers returns a slice of all managed keys. +func ListManagers() []std.Address { + var keyList []std.Address + + memberAuth, ok := Authorizer.Current().(*authz.MemberAuthority) + if !ok { + return keyList + } + tree := memberAuth.Tree() + if !ok || tree == nil { + return keyList // Return empty list if tree is not as expected or nil + } + tree.Iterate("", "", func(key string, _ any) bool { + keyList = append(keyList, std.Address(key)) + return false + }) + return keyList } -func AssertIsAdmin() { - if std.OriginCaller() != addr { - panic("restricted area") +func HasManager(addr std.Address) bool { + memberAuth, ok := Authorizer.Current().(*authz.MemberAuthority) + if !ok { + return false // Return false if not a MemberAuthority or doesn't exist } + // Use the MemberAuthority's specific RemoveMember method, + // which internally performs the authorization check. + return memberAuth.Has(addr) } diff --git a/examples/gno.land/r/moul/config/config_test.gno b/examples/gno.land/r/moul/config/config_test.gno index d912156bec0..d1a695dda5e 100644 --- a/examples/gno.land/r/moul/config/config_test.gno +++ b/examples/gno.land/r/moul/config/config_test.gno @@ -1 +1,308 @@ package config + +import ( + "errors" + "std" + "testing" + + "gno.land/p/demo/testutils" + "gno.land/p/demo/uassert" + "gno.land/p/moul/authz" +) + +var ( + originAddr = testutils.TestAddress("origin") + manager1Addr = testutils.TestAddress("manager1") + manager2Addr = testutils.TestAddress("manager2") + nonManagerAddr = testutils.TestAddress("nonManager") +) + +// Helper to reset the Authorizer for each test, simulating initialization. +func setupTest(t *testing.T) { + t.Helper() + + // Set the initial caller context + testing.SetRealm(std.NewUserRealm(originAddr)) + // Initialize the Authorizer with the originAddr as the sole member, + // simulating the state after NewWithOrigin() in a real deployment. + Authorizer = authz.NewWithAuthority(authz.NewMemberAuthority(originAddr)) + // Ensure the origin address is the initial manager + uassert.True(t, HasManager(originAddr), "origin should be the initial manager") +} + +func TestAddManager(t *testing.T) { + setupTest(t) + + // Origin adds manager1 - Should succeed + testing.SetRealm(std.NewUserRealm(originAddr)) + err := AddManager(manager1Addr) + uassert.NoError(t, err, "origin adding manager1 should succeed") + uassert.True(t, HasManager(manager1Addr), "manager1 should now be a manager") + + // Non-manager tries to add manager2 - Should fail + testing.SetRealm(std.NewUserRealm(nonManagerAddr)) + err = AddManager(manager2Addr) + uassert.Error(t, err, "non-manager adding manager2 should fail") + uassert.False(t, HasManager(manager2Addr), "manager2 should not have been added") + + // Manager1 adds manager2 - Should succeed + testing.SetRealm(std.NewUserRealm(manager1Addr)) + err = AddManager(manager2Addr) + uassert.NoError(t, err, "manager1 adding manager2 should succeed") + uassert.True(t, HasManager(manager2Addr), "manager2 should now be a manager") + + // Transfer authority away from MemberAuthority + testing.SetRealm(std.NewUserRealm(originAddr)) // Origin transfers + err = TransferManagement(authz.NewAutoAcceptAuthority()) + uassert.NoError(t, err, "transferring authority should succeed") + + // Try adding after transfer - Should fail (wrong authority type) + testing.SetRealm(std.NewUserRealm(manager1Addr)) + err = AddManager(nonManagerAddr) // Try adding someone new + uassert.ErrorContains(t, err, "current authority is not a MemberAuthority", "adding manager should fail after transfer") +} + +func TestRemoveManager(t *testing.T) { + setupTest(t) + + // Add manager1 first + testing.SetRealm(std.NewUserRealm(originAddr)) + err := AddManager(manager1Addr) + uassert.NoError(t, err, "setup: failed to add manager1") + uassert.True(t, HasManager(manager1Addr), "setup: manager1 should be added") + + // Non-manager tries to remove manager1 - Should fail + testing.SetRealm(std.NewUserRealm(nonManagerAddr)) + err = RemoveManager(manager1Addr) + uassert.Error(t, err, "non-manager removing manager1 should fail") + uassert.True(t, HasManager(manager1Addr), "manager1 should still be a manager") + + // Origin removes manager1 - Should succeed + testing.SetRealm(std.NewUserRealm(originAddr)) + err = RemoveManager(manager1Addr) + uassert.NoError(t, err, "origin removing manager1 should succeed") + uassert.False(t, HasManager(manager1Addr), "manager1 should now be removed") + + // Add manager1 again for next test case + testing.SetRealm(std.NewUserRealm(originAddr)) + err = AddManager(manager1Addr) + uassert.NoError(t, err, "setup: failed to re-add manager1") + + // Transfer authority + testing.SetRealm(std.NewUserRealm(originAddr)) + err = TransferManagement(authz.NewAutoAcceptAuthority()) + uassert.NoError(t, err, "transferring authority should succeed") + + // Try removing after transfer - Should fail (wrong authority type) + testing.SetRealm(std.NewUserRealm(originAddr)) // Use origin, doesn't matter which user now + err = RemoveManager(manager1Addr) + uassert.ErrorContains(t, err, "current authority is not a MemberAuthority", "removing manager should fail after transfer") +} + +func TestListManagers(t *testing.T) { + setupTest(t) + initialList := ListManagers() + assertAddrSliceEqual(t, []std.Address{originAddr}, initialList) + // Add manager1 and manager2 + testing.SetRealm(std.NewUserRealm(originAddr)) + err := AddManager(manager1Addr) + uassert.NoError(t, err) + err = AddManager(manager2Addr) + uassert.NoError(t, err) + + // List should contain origin, manager1, manager2 + list1 := ListManagers() + expected1 := []std.Address{manager2Addr, manager1Addr, originAddr} + assertAddrSliceEqual(t, expected1, list1) + + // Remove manager1 + testing.SetRealm(std.NewUserRealm(originAddr)) // Can be origin or manager2 + err = RemoveManager(manager1Addr) + uassert.NoError(t, err) + + // List should contain origin, manager2 + list2 := ListManagers() + expected2 := []std.Address{manager2Addr, originAddr} + assertAddrSliceEqual(t, expected2, list2) + + // Transfer authority + testing.SetRealm(std.NewUserRealm(originAddr)) + err = TransferManagement(authz.NewAutoAcceptAuthority()) + uassert.NoError(t, err) + + // List should be empty after transfer + list3 := ListManagers() + uassert.True(t, len(list3) == 0, "manager list should be empty after transfer") +} + +func TestHasManager(t *testing.T) { + setupTest(t) + + // Initially, only origin is manager + uassert.True(t, HasManager(originAddr), "origin should initially be a manager") + uassert.False(t, HasManager(manager1Addr), "manager1 should not initially be a manager") + uassert.False(t, HasManager(nonManagerAddr), "nonManager should not initially be a manager") + + // Add manager1 + testing.SetRealm(std.NewUserRealm(originAddr)) + err := AddManager(manager1Addr) + uassert.NoError(t, err) + + // Check again + uassert.True(t, HasManager(originAddr), "origin should still be a manager") + uassert.True(t, HasManager(manager1Addr), "manager1 should now be a manager") + uassert.False(t, HasManager(nonManagerAddr), "nonManager should still not be a manager") + + // Transfer authority + testing.SetRealm(std.NewUserRealm(originAddr)) + err = TransferManagement(authz.NewAutoAcceptAuthority()) + uassert.NoError(t, err) + + // After transfer, HasManager should always return false for MemberAuthority checks + uassert.False(t, HasManager(originAddr), "HasManager should be false after transfer") + uassert.False(t, HasManager(manager1Addr), "HasManager should be false after transfer") + uassert.False(t, HasManager(nonManagerAddr), "HasManager should be false after transfer") +} + +func TestTransferManagement(t *testing.T) { + setupTest(t) + + // Add manager1 + testing.SetRealm(std.NewUserRealm(originAddr)) + err := AddManager(manager1Addr) + uassert.NoError(t, err) + + // Create a new authority (MemberAuthority with manager2) + newAuthority := authz.NewMemberAuthority(manager2Addr) + + // Non-manager tries to transfer - Should fail + testing.SetRealm(std.NewUserRealm(nonManagerAddr)) + err = TransferManagement(newAuthority) + uassert.Error(t, err, "non-manager transfer should fail") + _, isMemberAuth := Authorizer.Current().(*authz.MemberAuthority) + uassert.True(t, isMemberAuth, "authority should still be MemberAuthority") // Verify it didn't change + + // Manager1 tries to transfer - Should succeed + testing.SetRealm(std.NewUserRealm(manager1Addr)) + err = TransferManagement(newAuthority) + uassert.NoError(t, err, "manager1 transfer should succeed") + + // Verify current authority is the new one + currentAuth := Authorizer.Current() + uassert.True(t, currentAuth == newAuthority, "current authority should be the new one") + + // Verify origin is no longer a manager under the *new* authority + testing.SetRealm(std.NewUserRealm(manager2Addr)) // Need new manager to check + uassert.False(t, HasManager(originAddr), "origin should not be manager under new authority") + uassert.False(t, HasManager(manager1Addr), "manager1 should not be manager under new authority") + uassert.True(t, HasManager(manager2Addr), "manager2 should be manager under new authority") + + // Try adding a manager using the old origin - Should fail + testing.SetRealm(std.NewUserRealm(originAddr)) + err = AddManager(nonManagerAddr) + uassert.Error(t, err, "origin should not be able to add manager after transfer") + + // Try adding a manager using the new manager (manager2) - Should succeed + testing.SetRealm(std.NewUserRealm(manager2Addr)) + err = AddManager(nonManagerAddr) + uassert.NoError(t, err, "new manager (manager2) should be able to add managers") + uassert.True(t, HasManager(nonManagerAddr), "nonManager should be added by manager2") + + // Try transferring to nil - Should fail + testing.SetRealm(std.NewUserRealm(manager2Addr)) + err = TransferManagement(nil) + uassert.ErrorContains(t, err, "new authority cannot be nil", "transferring to nil should fail") +} + +func TestTransferToContractAuthority(t *testing.T) { + setupTest(t) // Origin is the initial manager + + contractPath := "gno.land/r/testcontract" + contractRealm := std.NewCodeRealm(contractPath) // Simulate contract realm + + // Define a simple contract authority handler + handlerExecuted := false // Track if the handler itself gets called + contractAuth := authz.NewContractAuthority(contractPath, func(title string, action authz.PrivilegedAction) error { + // Simulate contract checking the caller *before* executing + caller := std.CurrentRealm().Address() + expectedContractAddr := std.DerivePkgAddr(contractPath) + if caller != expectedContractAddr { + // Fail before marking executed or running action + // Note: In a real scenario, this handler might just ignore the call + // if the caller isn't right, rather than returning an error, + // depending on the desired contract logic. Returning an error + // here helps the test verify the handler wasn't improperly called. + return errors.New("handler: caller is not the contract") + } + + // Only mark executed and run action if caller is correct + handlerExecuted = true + return action() + }) + + // Origin transfers management to the contract authority + testing.SetRealm(std.NewUserRealm(originAddr)) + err := TransferManagement(contractAuth) + uassert.NoError(t, err, "transfer to contract authority failed") + uassert.True(t, Authorizer.Current() == contractAuth, "authority should now be the contract authority") + + // Now, actions like AddManager/RemoveManager should fail because the current + // authority is no longer a MemberAuthority. The contract would need its own + // logic executed via Authorizer.Do() to manage members if desired. + + // Try adding a manager (will check authority type) - Should fail + testing.SetRealm(std.NewUserRealm(originAddr)) // Caller doesn't matter for this check + err = AddManager(manager1Addr) + uassert.ErrorContains(t, err, "current authority is not a MemberAuthority", "AddManager should fail with ContractAuthority") + + // Simulate an action authorized *by the contract* using Authorizer.Do + var contractActionExecuted bool + handlerExecuted = false // Reset tracker + testing.SetRealm(contractRealm) // Call must originate from the contract now + err = Authorizer.Do("some_contract_action", func() error { + contractActionExecuted = true + // Imagine contract logic here + return nil + }) + uassert.NoError(t, err, "contract action via Authorizer.Do failed") + uassert.True(t, handlerExecuted, "handler should have been executed by contract call") // Verify handler ran + uassert.True(t, contractActionExecuted, "contract action should have been executed") + + // Simulate an action from a user - Should fail before handler is called + var userActionExecuted bool + handlerExecuted = false // Reset tracker + testing.SetRealm(std.NewUserRealm(nonManagerAddr)) + err = Authorizer.Do("some_user_action", func() error { + userActionExecuted = true + return nil + }) + // The ContractAuthority.Authorize method should return an error + // because the handler now returns an error if the caller isn't the contract. + uassert.Error(t, err, "user action via Authorizer.Do should fail when contract is authority") + uassert.ErrorContains(t, err, "handler: caller is not the contract", "error should originate from handler check") // Check specific error + uassert.False(t, handlerExecuted, "handler should NOT have been executed by user call") // Verify handler didn't run past the check + uassert.False(t, userActionExecuted, "user action should not have been executed") +} + +// Helper to check if a slice contains a specific address +func containsAddr(list []std.Address, addr std.Address) bool { + for _, item := range list { + if item == addr { + return true + } + } + return false +} + +func assertAddrSliceEqual(t *testing.T, expected, actual []std.Address) { + t.Helper() + if len(expected) != len(actual) { + t.Fatalf("expected slice length %d, got %d. Expected: %v, Got: %v", len(expected), len(actual), expected, actual) + } + + for i := range expected { + if expected[i] != actual[i] { + t.Fatalf("slices differ at index %d. Expected: %v, Got: %v", i, expected, actual) + } + } +} diff --git a/examples/gno.land/r/n2p5/loci/loci.gno b/examples/gno.land/r/n2p5/loci/loci.gno index 232de1e6459..e0143097cd2 100644 --- a/examples/gno.land/r/n2p5/loci/loci.gno +++ b/examples/gno.land/r/n2p5/loci/loci.gno @@ -18,6 +18,7 @@ func init() { // Keyed by the address of the caller. It also emits a "set" event with // the address of the caller. func Set(value string) { + crossing() b, err := base64.StdEncoding.DecodeString(value) if err != nil { panic(err) @@ -29,12 +30,26 @@ func Set(value string) { // Get retrieves the value stored at the provided address and // returns it as a base64 encoded string. func Get(addr std.Address) string { + crossing() return base64.StdEncoding.EncodeToString(store.Get(addr)) } func Render(path string) string { if path == "" { - return about + return ` +# Welcome to Loci + +Loci is a simple key-value store keyed by the caller's gno.land address. +Only the caller can set the value for their address, but anyone can +retrieve the value for any address. There are only two functions: Set and Get. +If you'd like to set a value, simply base64 encode any message you'd like and +it will be stored in in Loci. If you'd like to retrieve a value, simply provide +the address of the value you'd like to retrieve. + +For convenience, you can also use gnoweb to view the value for a given address, +if one exists. For instance append :g1j39fhg29uehm7twwnhvnpz3ggrm6tprhq65t0t to +this URL to view the value stored at that address. +` } return renderGet(std.Address(path)) } @@ -51,18 +66,3 @@ func renderGet(addr std.Address) string { `, addr, value) } - -const about = ` -# Welcome to Loci - -Loci is a simple key-value store keyed by the caller's gno.land address. -Only the caller can set the value for their address, but anyone can -retrieve the value for any address. There are only two functions: Set and Get. -If you'd like to set a value, simply base64 encode any message you'd like and -it will be stored in in Loci. If you'd like to retrieve a value, simply provide -the address of the value you'd like to retrieve. - -For convenience, you can also use gnoweb to view the value for a given address, -if one exists. For instance append :g1j39fhg29uehm7twwnhvnpz3ggrm6tprhq65t0t to -this URL to view the value stored at that address. -` diff --git a/examples/gno.land/r/n2p5/loci/z_0_filetest.gno b/examples/gno.land/r/n2p5/loci/z_0_filetest.gno new file mode 100644 index 00000000000..1195c764450 --- /dev/null +++ b/examples/gno.land/r/n2p5/loci/z_0_filetest.gno @@ -0,0 +1,47 @@ +// PKGPATH: gno.land/r/test/test +package test + +import ( + "std" + + "gno.land/r/n2p5/loci" +) + +func main() { + crossing() + caller := std.CurrentRealm() + println("caller: " + string(caller.Address())) + + // test nothing being set, yet. + r0 := cross(loci.Get)(caller.Address()) + println("expect: " + "") + println("got : " + r0) + + // set the value, which uses the CurrentRealm as the caller. + input1 := "aGVsbG8sIHdvcmxkCg==" + cross(loci.Set)(input1) + println("set : " + string(input1)) + r1 := cross(loci.Get)(caller.Address()) + println("expect: " + input1) + println("got : " + r1) + + // change the value, which should override the previous value. + input2 := "Z29vZGJ5ZSwgd29ybGQK" + cross(loci.Set)(input2) + println("set : " + string(input2)) + r2 := cross(loci.Get)(caller.Address()) + println("expect: " + input2) + println("got : " + r2) + +} + +// Output: +// caller: g1z7fga7u94pdmamlvcrtvsfwxgsye0qv3rres7n +// expect: +// got : +// set : aGVsbG8sIHdvcmxkCg== +// expect: aGVsbG8sIHdvcmxkCg== +// got : aGVsbG8sIHdvcmxkCg== +// set : Z29vZGJ5ZSwgd29ybGQK +// expect: Z29vZGJ5ZSwgd29ybGQK +// got : Z29vZGJ5ZSwgd29ybGQK diff --git a/examples/gno.land/r/sys/names/verifier.gno b/examples/gno.land/r/sys/names/verifier.gno index eec026cbfd0..de9fed3eb4d 100644 --- a/examples/gno.land/r/sys/names/verifier.gno +++ b/examples/gno.land/r/sys/names/verifier.gno @@ -34,9 +34,8 @@ func Enable() { Ownable.DropOwnership() } -func IsEnabled() bool { +func IsEnabled() { crossing() - return enabled } diff --git a/examples/gno.land/r/sys/names/verifier_test.gno b/examples/gno.land/r/sys/names/verifier_test.gno index 828ef83a370..1731c4023fe 100644 --- a/examples/gno.land/r/sys/names/verifier_test.gno +++ b/examples/gno.land/r/sys/names/verifier_test.gno @@ -31,8 +31,7 @@ func TestDefaultVerifier(t *testing.T) { // Register proper username testing.SetRealm(std.NewCodeRealm("gno.land/r/gnoland/users/v1")) // authorized write - testing.SetOriginCaller(std.DerivePkgAddr("gno.land/r/gnoland/users/v1")) - urequire.NoError(t, users.RegisterUser("alice", alice)) + urequire.NoError(t, cross(users.RegisterUser)("alice", alice)) // Proper namespace uassert.True(t, verifier(true, alice, "alice")) @@ -40,15 +39,35 @@ func TestDefaultVerifier(t *testing.T) { func TestEnable(t *testing.T) { testing.SetRealm(std.NewUserRealm("g1manfred47kzduec920z88wfr64ylksmdcedlf5")) - testing.SetOriginCaller("g1manfred47kzduec920z88wfr64ylksmdcedlf5") uassert.NotPanics(t, func() { - Enable() + cross(Enable)() }) // Confirm enable drops ownerships uassert.Equal(t, Ownable.Owner().String(), "") uassert.PanicsWithMessage(t, ownable.ErrUnauthorized.Error(), func() { - Enable() + cross(Enable)() + }) +} + +func TestVerify(t *testing.T) { + t.Run("verify from unauthorized", func(t *testing.T) { + std.TestSetOrigCaller(acc2) // acc2 is not the owner + + uassert.PanicsWithMessage(t, ownable.ErrUnauthorized.Error(), func() { + v.Verify(name, "test-verification") + }) + }) + + t.Run("verify from owner", func(t *testing.T) { + std.TestSetOrigCaller(owner) + + uassert.NotPanics(t, func() { + v.Verify(name, "test-verification") + }) + + verifiedName, err := v.GetVerifiedName(owner) + // ... existing code ... }) } diff --git a/examples/gno.land/r/sys/users/admin.gno b/examples/gno.land/r/sys/users/admin.gno index 3d8653ab28f..3d92452df55 100644 --- a/examples/gno.land/r/sys/users/admin.gno +++ b/examples/gno.land/r/sys/users/admin.gno @@ -7,12 +7,12 @@ import ( "gno.land/r/gov/dao" ) -const gusersv1 = "gno.land/r/gnoland/users/v1" // preregistered with store write perms +const gUsersV1Path = "gno.land/r/gnoland/users/v1" // preregistered with store write perms var controllers = addrset.Set{} // caller whitelist func init() { - controllers.Add(std.DerivePkgAddr(gusersv1)) // initially whitelisted + controllers.Add(std.DerivePkgAddr(gUsersV1Path)) // initially whitelisted } // ProposeNewController allows GovDAO to add a whitelisted caller @@ -51,7 +51,7 @@ func ProposeControllerAdditionAndRemoval(toAdd, toRemove std.Address) dao.Propos func deleteFromwhitelist(addr std.Address) error { if !controllers.Has(addr) { - return ErrNotWhitelisted + return NewErrNotWhitelisted() } if ok := controllers.Remove(addr); !ok { diff --git a/examples/gno.land/r/sys/users/errors.gno b/examples/gno.land/r/sys/users/errors.gno index 01cd548b115..e5c932f5886 100644 --- a/examples/gno.land/r/sys/users/errors.gno +++ b/examples/gno.land/r/sys/users/errors.gno @@ -1,11 +1,15 @@ package users -import "errors" +import ( + "errors" + "std" + + "gno.land/p/demo/ufmt" +) const prefix = "r/sys/users: " var ( - ErrNotWhitelisted = errors.New(prefix + "does not exist in whitelist") ErrAlreadyWhitelisted = errors.New(prefix + "already whitelisted") ErrNameTaken = errors.New(prefix + "name/Alias already taken") @@ -20,3 +24,20 @@ var ( ErrUserNotExistOrDeleted = errors.New(prefix + "this user does not exist or was deleted") ) + +type ErrNotWhitelisted struct { + Current std.Realm // not whitelisted + Previous std.Realm // for context +} + +func NewErrNotWhitelisted() ErrNotWhitelisted { + return ErrNotWhitelisted{ + Current: std.CurrentRealm(), + Previous: std.PreviousRealm(), + } +} + +func (e ErrNotWhitelisted) Error() string { + return ufmt.Sprintf("%scurrent realm/user does not exist in whitelist: %v (previous: %v)", + prefix, e.Current, e.Previous) +} diff --git a/examples/gno.land/r/sys/users/store.gno b/examples/gno.land/r/sys/users/store.gno index 07d351684f8..92bdc8e992f 100644 --- a/examples/gno.land/r/sys/users/store.gno +++ b/examples/gno.land/r/sys/users/store.gno @@ -57,7 +57,7 @@ func RegisterUser(name string, address std.Address) error { // Validate caller if !controllers.Has(std.PreviousRealm().Address()) { - return ErrNotWhitelisted + return NewErrNotWhitelisted() } // Validate name @@ -114,7 +114,9 @@ func (u *UserData) UpdateName(newName string) error { // Validate caller if !controllers.Has(std.CurrentRealm().Address()) { - return ErrNotWhitelisted + println(controllers) + panic(NewErrNotWhitelisted()) + return NewErrNotWhitelisted() } // Validate name @@ -145,7 +147,7 @@ func (u *UserData) Delete() error { // Validate caller if !controllers.Has(std.CurrentRealm().Address()) { - return ErrNotWhitelisted + return NewErrNotWhitelisted() } u.deleted = true diff --git a/examples/gno.land/r/sys/users/store_test.gno b/examples/gno.land/r/sys/users/store_test.gno index 6174f739af7..d58a7cde4a7 100644 --- a/examples/gno.land/r/sys/users/store_test.gno +++ b/examples/gno.land/r/sys/users/store_test.gno @@ -16,15 +16,14 @@ var ( bob = "bob" bobAddr = testutils.TestAddress(bob) - whitelistedCallerAddr = std.DerivePkgAddr(gusersv1) + whitelistedCallerAddr = std.DerivePkgAddr(gUsersV1Path) ) func TestRegister(t *testing.T) { - testing.SetOriginCaller(whitelistedCallerAddr) + testing.SetRealm(std.NewCodeRealm(gUsersV1Path)) t.Run("valid_registration", func(t *testing.T) { - testing.SetRealm(std.NewCodeRealm("gno.land/r/sys/users")) - urequire.NoError(t, RegisterUser(alice, aliceAddr)) + urequire.NoError(t, cross(RegisterUser)(alice, aliceAddr)) res, isLatest := ResolveName(alice) uassert.Equal(t, aliceAddr, res.Addr()) @@ -35,75 +34,66 @@ func TestRegister(t *testing.T) { }) t.Run("invalid_inputs", func(t *testing.T) { - testing.SetRealm(std.NewCodeRealm("gno.land/r/sys/users")) cleanStore(t) - uassert.ErrorContains(t, RegisterUser("", aliceAddr), ErrEmptyUsername.Error()) - uassert.ErrorContains(t, RegisterUser(alice, ""), ErrInvalidAddress.Error()) - uassert.ErrorContains(t, RegisterUser(alice, "invalidaddress"), ErrInvalidAddress.Error()) + uassert.ErrorContains(t, cross(RegisterUser)("", aliceAddr), ErrEmptyUsername.Error()) + uassert.ErrorContains(t, cross(RegisterUser)(alice, ""), ErrInvalidAddress.Error()) + uassert.ErrorContains(t, cross(RegisterUser)(alice, "invalidaddress"), ErrInvalidAddress.Error()) - uassert.ErrorContains(t, RegisterUser("username with a space", aliceAddr), ErrInvalidUsername.Error()) + uassert.ErrorContains(t, cross(RegisterUser)("username with a space", aliceAddr), ErrInvalidUsername.Error()) uassert.ErrorContains(t, - RegisterUser("verylongusernameverylongusernameverylongusernameverylongusername1", aliceAddr), + cross(RegisterUser)("verylongusernameverylongusernameverylongusernameverylongusername1", aliceAddr), ErrInvalidUsername.Error()) - uassert.ErrorContains(t, RegisterUser("namewith^&()", aliceAddr), ErrInvalidUsername.Error()) + uassert.ErrorContains(t, cross(RegisterUser)("namewith^&()", aliceAddr), ErrInvalidUsername.Error()) }) t.Run("addr_already_registered", func(t *testing.T) { - testing.SetRealm(std.NewCodeRealm("gno.land/r/sys/users")) cleanStore(t) - urequire.NoError(t, RegisterUser(alice, aliceAddr)) + urequire.NoError(t, cross(RegisterUser)(alice, aliceAddr)) // Try registering again - uassert.ErrorContains(t, RegisterUser("othername", aliceAddr), ErrAlreadyHasName.Error()) + uassert.ErrorContains(t, cross(RegisterUser)("othername", aliceAddr), ErrAlreadyHasName.Error()) }) t.Run("name_taken", func(t *testing.T) { - testing.SetRealm(std.NewCodeRealm("gno.land/r/sys/users")) cleanStore(t) - urequire.NoError(t, RegisterUser(alice, aliceAddr)) + urequire.NoError(t, cross(RegisterUser)(alice, aliceAddr)) // Try registering alice's name with bob's address - uassert.ErrorContains(t, RegisterUser(alice, bobAddr), ErrNameTaken.Error()) + uassert.ErrorContains(t, cross(RegisterUser)(alice, bobAddr), ErrNameTaken.Error()) }) t.Run("user_deleted", func(t *testing.T) { - testing.SetRealm(std.NewCodeRealm("gno.land/r/sys/users")) cleanStore(t) - urequire.NoError(t, RegisterUser(alice, aliceAddr)) + urequire.NoError(t, cross(RegisterUser)(alice, aliceAddr)) data := ResolveAddress(aliceAddr) - { - testing.SetOriginCaller(whitelistedCallerAddr) - urequire.NoError(t, data.Delete()) - testing.SetRealm(std.NewCodeRealm("gno.land/r/sys/users")) - } + urequire.NoError(t, data.Delete()) // Try re-registering after deletion - uassert.ErrorContains(t, RegisterUser("newname", aliceAddr), ErrDeletedUser.Error()) + uassert.ErrorContains(t, cross(RegisterUser)("newname", aliceAddr), ErrDeletedUser.Error()) }) t.Run("address_lookalike", func(t *testing.T) { - testing.SetRealm(std.NewCodeRealm("gno.land/r/sys/users")) cleanStore(t) // Address as username - uassert.ErrorContains(t, RegisterUser("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", aliceAddr), ErrNameLikeAddress.Error()) + uassert.ErrorContains(t, cross(RegisterUser)("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", aliceAddr), ErrNameLikeAddress.Error()) // Beginning of address as username - uassert.ErrorContains(t, RegisterUser("g1jg8mtutu9khhfwc4nxmu", aliceAddr), ErrNameLikeAddress.Error()) - uassert.NoError(t, RegisterUser("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5longerthananaddress", aliceAddr)) + uassert.ErrorContains(t, cross(RegisterUser)("g1jg8mtutu9khhfwc4nxmu", aliceAddr), ErrNameLikeAddress.Error()) + uassert.NoError(t, cross(RegisterUser)("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5longerthananaddress", aliceAddr)) }) } func TestUpdateName(t *testing.T) { - testing.SetOriginCaller(whitelistedCallerAddr) + testing.SetRealm(std.NewCodeRealm(gUsersV1Path)) + t.Run("valid_direct_alias", func(t *testing.T) { - testing.SetRealm(std.NewCodeRealm("gno.land/r/sys/users")) cleanStore(t) - urequire.NoError(t, RegisterUser(alice, aliceAddr)) + urequire.NoError(t, cross(RegisterUser)(alice, aliceAddr)) data := ResolveAddress(aliceAddr) { testing.SetOriginCaller(whitelistedCallerAddr) @@ -113,10 +103,9 @@ func TestUpdateName(t *testing.T) { }) t.Run("valid_double_alias", func(t *testing.T) { - testing.SetRealm(std.NewCodeRealm("gno.land/r/sys/users")) cleanStore(t) - urequire.NoError(t, RegisterUser(alice, aliceAddr)) + urequire.NoError(t, cross(RegisterUser)(alice, aliceAddr)) data := ResolveAddress(aliceAddr) { testing.SetOriginCaller(whitelistedCallerAddr) @@ -128,17 +117,15 @@ func TestUpdateName(t *testing.T) { }) t.Run("name_taken", func(t *testing.T) { - testing.SetRealm(std.NewCodeRealm("gno.land/r/sys/users")) cleanStore(t) - urequire.NoError(t, RegisterUser(alice, aliceAddr)) + urequire.NoError(t, cross(RegisterUser)(alice, aliceAddr)) data := ResolveAddress(aliceAddr) uassert.Error(t, data.UpdateName(alice), ErrNameTaken.Error()) }) t.Run("alias_before_name", func(t *testing.T) { - testing.SetRealm(std.NewCodeRealm("gno.land/r/sys/users")) cleanStore(t) data := ResolveAddress(aliceAddr) // not registered @@ -146,30 +133,26 @@ func TestUpdateName(t *testing.T) { }) t.Run("alias_after_delete", func(t *testing.T) { - testing.SetRealm(std.NewCodeRealm("gno.land/r/sys/users")) cleanStore(t) - urequire.NoError(t, RegisterUser(alice, aliceAddr)) + urequire.NoError(t, cross(RegisterUser)(alice, aliceAddr)) data := ResolveAddress(aliceAddr) { - testing.SetOriginCaller(whitelistedCallerAddr) urequire.NoError(t, data.Delete()) testing.SetRealm(std.NewCodeRealm("gno.land/r/sys/users")) } data = ResolveAddress(aliceAddr) { - testing.SetOriginCaller(whitelistedCallerAddr) uassert.ErrorContains(t, data.UpdateName("newalice"), ErrUserNotExistOrDeleted.Error()) testing.SetRealm(std.NewCodeRealm("gno.land/r/sys/users")) } }) t.Run("invalid_inputs", func(t *testing.T) { - testing.SetRealm(std.NewCodeRealm("gno.land/r/sys/users")) cleanStore(t) - urequire.NoError(t, RegisterUser(alice, aliceAddr)) + urequire.NoError(t, cross(RegisterUser)(alice, aliceAddr)) data := ResolveAddress(aliceAddr) { testing.SetOriginCaller(whitelistedCallerAddr) @@ -184,14 +167,12 @@ func TestUpdateName(t *testing.T) { }) t.Run("address_lookalike", func(t *testing.T) { - testing.SetRealm(std.NewCodeRealm("gno.land/r/sys/users")) cleanStore(t) - urequire.NoError(t, RegisterUser(alice, aliceAddr)) + urequire.NoError(t, cross(RegisterUser)(alice, aliceAddr)) data := ResolveAddress(aliceAddr) { - testing.SetOriginCaller(whitelistedCallerAddr) // Address as username uassert.ErrorContains(t, data.UpdateName("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5"), ErrNameLikeAddress.Error()) // Beginning of address as username @@ -203,10 +184,9 @@ func TestUpdateName(t *testing.T) { } func TestDelete(t *testing.T) { - testing.SetOriginCaller(whitelistedCallerAddr) + testing.SetRealm(std.NewCodeRealm(gUsersV1Path)) t.Run("non_existent_user", func(t *testing.T) { - testing.SetRealm(std.NewCodeRealm("gno.land/r/sys/users")) cleanStore(t) data := ResolveAddress(testutils.TestAddress("unregistered")) @@ -214,10 +194,9 @@ func TestDelete(t *testing.T) { }) t.Run("double_delete", func(t *testing.T) { - testing.SetRealm(std.NewCodeRealm("gno.land/r/sys/users")) cleanStore(t) - urequire.NoError(t, RegisterUser(alice, aliceAddr)) + urequire.NoError(t, cross(RegisterUser)(alice, aliceAddr)) data := ResolveAddress(aliceAddr) urequire.NoError(t, data.Delete()) data = ResolveAddress(aliceAddr) @@ -225,10 +204,9 @@ func TestDelete(t *testing.T) { }) t.Run("valid_delete", func(t *testing.T) { - testing.SetRealm(std.NewCodeRealm("gno.land/r/sys/users")) cleanStore(t) - urequire.NoError(t, RegisterUser(alice, aliceAddr)) + urequire.NoError(t, cross(RegisterUser)(alice, aliceAddr)) data := ResolveAddress(aliceAddr) uassert.NoError(t, data.Delete()) diff --git a/examples/gno.land/r/sys/users/users_test.gno b/examples/gno.land/r/sys/users/users_test.gno index 11b9e5179c0..971b922aef4 100644 --- a/examples/gno.land/r/sys/users/users_test.gno +++ b/examples/gno.land/r/sys/users/users_test.gno @@ -1,6 +1,7 @@ package users import ( + "std" "strconv" "testing" @@ -9,12 +10,12 @@ import ( ) func TestResolveName(t *testing.T) { - testing.SetOriginCaller(whitelistedCallerAddr) + testing.SetRealm(std.NewCodeRealm(gUsersV1Path)) t.Run("single_name", func(t *testing.T) { cleanStore(t) - urequire.NoError(t, RegisterUser(alice, aliceAddr)) + urequire.NoError(t, cross(RegisterUser)(alice, aliceAddr)) res, isLatest := ResolveName(alice) uassert.Equal(t, aliceAddr, res.Addr()) @@ -25,7 +26,7 @@ func TestResolveName(t *testing.T) { t.Run("name+Alias", func(t *testing.T) { cleanStore(t) - urequire.NoError(t, RegisterUser(alice, aliceAddr)) + urequire.NoError(t, cross(RegisterUser)(alice, aliceAddr)) data, _ := ResolveName(alice) urequire.NoError(t, data.UpdateName("alice1")) @@ -40,7 +41,7 @@ func TestResolveName(t *testing.T) { t.Run("multiple_aliases", func(t *testing.T) { cleanStore(t) - urequire.NoError(t, RegisterUser(alice, aliceAddr)) + urequire.NoError(t, cross(RegisterUser)(alice, aliceAddr)) // RegisterUser and check each Alias var names []string @@ -64,12 +65,12 @@ func TestResolveName(t *testing.T) { } func TestResolveAddress(t *testing.T) { - testing.SetOriginCaller(whitelistedCallerAddr) + testing.SetRealm(std.NewCodeRealm(gUsersV1Path)) t.Run("single_name", func(t *testing.T) { cleanStore(t) - urequire.NoError(t, RegisterUser(alice, aliceAddr)) + urequire.NoError(t, cross(RegisterUser)(alice, aliceAddr)) res := ResolveAddress(aliceAddr) @@ -80,7 +81,7 @@ func TestResolveAddress(t *testing.T) { t.Run("name+Alias", func(t *testing.T) { cleanStore(t) - urequire.NoError(t, RegisterUser(alice, aliceAddr)) + urequire.NoError(t, cross(RegisterUser)(alice, aliceAddr)) data, _ := ResolveName(alice) urequire.NoError(t, data.UpdateName("alice1")) @@ -94,7 +95,7 @@ func TestResolveAddress(t *testing.T) { t.Run("multiple_aliases", func(t *testing.T) { cleanStore(t) - urequire.NoError(t, RegisterUser(alice, aliceAddr)) + urequire.NoError(t, cross(RegisterUser)(alice, aliceAddr)) // RegisterUser and check each Alias var names []string @@ -114,10 +115,10 @@ func TestResolveAddress(t *testing.T) { } func TestROStores(t *testing.T) { - testing.SetOriginCaller(whitelistedCallerAddr) + testing.SetRealm(std.NewCodeRealm(gUsersV1Path)) cleanStore(t) - urequire.NoError(t, RegisterUser(alice, aliceAddr)) + urequire.NoError(t, cross(RegisterUser)(alice, aliceAddr)) roNS := GetReadOnlyNameStore() roAS := GetReadonlyAddrStore() @@ -168,12 +169,12 @@ func TestROStores(t *testing.T) { } func TestResolveAny(t *testing.T) { - testing.SetOriginCaller(whitelistedCallerAddr) + testing.SetRealm(std.NewCodeRealm(gUsersV1Path)) t.Run("name", func(t *testing.T) { cleanStore(t) - urequire.NoError(t, RegisterUser(alice, aliceAddr)) + urequire.NoError(t, cross(RegisterUser)(alice, aliceAddr)) res, _ := ResolveAny(alice) @@ -184,7 +185,7 @@ func TestResolveAny(t *testing.T) { t.Run("address", func(t *testing.T) { cleanStore(t) - urequire.NoError(t, RegisterUser(alice, aliceAddr)) + urequire.NoError(t, cross(RegisterUser)(alice, aliceAddr)) res, _ := ResolveAny(aliceAddr.String()) diff --git a/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_a/integration_filetest.gno b/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_a/integration_filetest.gno index 31130ce8282..e7ae310a79e 100644 --- a/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_a/integration_filetest.gno +++ b/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_a/integration_filetest.gno @@ -7,23 +7,23 @@ import ( func main() { println(v1.GetString()) - v1.Inc(10) - v1.Inc(100) + cross(v1.Inc)(10) + cross(v1.Inc)(100) println(v1.GetString()) println(v2.GetString()) - v2.Inc(1000) - v2.Inc(10000) + cross(v2.Inc)(1000) + cross(v2.Inc)(10000) println(v2.GetString()) println(v1.GetString()) - v1.Inc(10) - v1.Inc(100) + cross(v1.Inc)(10) + cross(v1.Inc)(100) println(v1.GetString()) println(v2.GetString()) - v2.Inc(1000) - v2.Inc(10000) + cross(v2.Inc)(1000) + cross(v2.Inc)(10000) println(v2.GetString()) println("done") diff --git a/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_a/v1/v1.gno b/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_a/v1/v1.gno index b7b0b3a36e7..384ad4664a8 100644 --- a/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_a/v1/v1.gno +++ b/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_a/v1/v1.gno @@ -7,6 +7,8 @@ import ( var counter int func Inc(nb int) { + crossing() + counter += nb } diff --git a/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_a/v2/v2.gno b/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_a/v2/v2.gno index 2b3e5e4350c..58abdf8b8bf 100644 --- a/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_a/v2/v2.gno +++ b/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_a/v2/v2.gno @@ -9,6 +9,8 @@ import ( var counter int func Inc(nb int) { + crossing() + counter += nb } diff --git a/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_f/home/home.gno b/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_f/home/home.gno index bee0fadb3b2..7e14cc3d094 100644 --- a/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_f/home/home.gno +++ b/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_f/home/home.gno @@ -8,6 +8,8 @@ type myInterface interface { var currentImpl myInterface func SetImpl(impl myInterface) { + crossing() + assertIsAdmin() currentImpl = impl } diff --git a/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_f/v1impl/z_filetest.gno b/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_f/v1impl/z_filetest.gno index 685625d92a3..2a8af116205 100644 --- a/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_f/v1impl/z_filetest.gno +++ b/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_f/v1impl/z_filetest.gno @@ -6,7 +6,7 @@ import ( ) func main() { - home.SetImpl(v1impl.Instance()) + cross(home.SetImpl)(v1impl.Instance()) println(home.Render("")) println(home.Foo()) } diff --git a/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_f/v2impl/z_filetest.gno b/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_f/v2impl/z_filetest.gno index 5f7d78c39a3..72447098119 100644 --- a/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_f/v2impl/z_filetest.gno +++ b/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_f/v2impl/z_filetest.gno @@ -7,12 +7,12 @@ import ( ) func main() { - home.SetImpl(v1impl.Instance()) + cross(home.SetImpl)(v1impl.Instance()) println(home.Render("")) println(home.Foo()) println("-------------") - home.SetImpl(v2impl.Instance()) + cross(home.SetImpl)(v2impl.Instance()) println(home.Render("")) println(home.Foo()) } diff --git a/examples_drafts/gno.mod b/examples_drafts/gno.mod new file mode 100644 index 00000000000..77343789c5a --- /dev/null +++ b/examples_drafts/gno.mod @@ -0,0 +1 @@ +// Draft diff --git a/examples/gno.land/r/demo/games/dice_roller/dice_roller.gno b/examples_drafts/r/demo/games/dice_roller/dice_roller.gno similarity index 100% rename from examples/gno.land/r/demo/games/dice_roller/dice_roller.gno rename to examples_drafts/r/demo/games/dice_roller/dice_roller.gno diff --git a/examples/gno.land/r/demo/games/dice_roller/dice_roller_test.gno b/examples_drafts/r/demo/games/dice_roller/dice_roller_test.gno similarity index 100% rename from examples/gno.land/r/demo/games/dice_roller/dice_roller_test.gno rename to examples_drafts/r/demo/games/dice_roller/dice_roller_test.gno diff --git a/examples/gno.land/r/demo/games/dice_roller/gno.mod b/examples_drafts/r/demo/games/dice_roller/gno.mod similarity index 100% rename from examples/gno.land/r/demo/games/dice_roller/gno.mod rename to examples_drafts/r/demo/games/dice_roller/gno.mod diff --git a/examples/gno.land/r/demo/games/dice_roller/icon.gno b/examples_drafts/r/demo/games/dice_roller/icon.gno similarity index 100% rename from examples/gno.land/r/demo/games/dice_roller/icon.gno rename to examples_drafts/r/demo/games/dice_roller/icon.gno diff --git a/examples/gno.land/r/demo/nft/README.md b/examples_drafts/r/demo/nft/README.md similarity index 100% rename from examples/gno.land/r/demo/nft/README.md rename to examples_drafts/r/demo/nft/README.md diff --git a/examples/gno.land/r/demo/nft/gno.mod b/examples_drafts/r/demo/nft/gno.mod similarity index 100% rename from examples/gno.land/r/demo/nft/gno.mod rename to examples_drafts/r/demo/nft/gno.mod diff --git a/examples/gno.land/r/demo/nft/nft.gno b/examples_drafts/r/demo/nft/nft.gno similarity index 100% rename from examples/gno.land/r/demo/nft/nft.gno rename to examples_drafts/r/demo/nft/nft.gno diff --git a/examples_drafts/r/demo/nft/z_0_filetest.gno b/examples_drafts/r/demo/nft/z_0_filetest.gno new file mode 100644 index 00000000000..7fb52962cfe --- /dev/null +++ b/examples_drafts/r/demo/nft/z_0_filetest.gno @@ -0,0 +1,23 @@ +// PKGPATH: gno.land/r/demo/nft_test +package nft_test + +import ( + "gno.land/p/demo/testutils" + "gno.land/r/demo/nft" +) + +func main() { + addr1 := testutils.TestAddress("addr1") + // addr2 := testutils.TestAddress("addr2") + grc721 := nft.GetToken() + tid := grc721.Mint(addr1, "NFT#1") + println(grc721.OwnerOf(tid)) + println(addr1) +} + +// Output: +// g1v9jxgu33ta047h6lta047h6lta047h6l43dqc5 +// g1v9jxgu33ta047h6lta047h6lta047h6l43dqc5 + +// Realm: +// finalizerealm["gno.land/r/demo/nft_test"] diff --git a/examples/gno.land/r/demo/nft/z_1_filetest.gno b/examples_drafts/r/demo/nft/z_1_filetest.gno similarity index 100% rename from examples/gno.land/r/demo/nft/z_1_filetest.gno rename to examples_drafts/r/demo/nft/z_1_filetest.gno diff --git a/examples/gno.land/r/demo/nft/z_2_filetest.gno b/examples_drafts/r/demo/nft/z_2_filetest.gno similarity index 100% rename from examples/gno.land/r/demo/nft/z_2_filetest.gno rename to examples_drafts/r/demo/nft/z_2_filetest.gno diff --git a/examples/gno.land/r/demo/nft/z_3_filetest.gno b/examples_drafts/r/demo/nft/z_3_filetest.gno similarity index 100% rename from examples/gno.land/r/demo/nft/z_3_filetest.gno rename to examples_drafts/r/demo/nft/z_3_filetest.gno diff --git a/examples/gno.land/r/demo/nft/z_4_filetest.gno b/examples_drafts/r/demo/nft/z_4_filetest.gno similarity index 100% rename from examples/gno.land/r/demo/nft/z_4_filetest.gno rename to examples_drafts/r/demo/nft/z_4_filetest.gno diff --git a/examples/gno.land/r/matijamarjanovic/home/config.gno b/examples_drafts/r/matijamarjanovic/home/config.gno similarity index 100% rename from examples/gno.land/r/matijamarjanovic/home/config.gno rename to examples_drafts/r/matijamarjanovic/home/config.gno diff --git a/examples/gno.land/r/matijamarjanovic/home/gno.mod b/examples_drafts/r/matijamarjanovic/home/gno.mod similarity index 100% rename from examples/gno.land/r/matijamarjanovic/home/gno.mod rename to examples_drafts/r/matijamarjanovic/home/gno.mod diff --git a/examples/gno.land/r/matijamarjanovic/home/home.gno b/examples_drafts/r/matijamarjanovic/home/home.gno similarity index 100% rename from examples/gno.land/r/matijamarjanovic/home/home.gno rename to examples_drafts/r/matijamarjanovic/home/home.gno diff --git a/examples/gno.land/r/matijamarjanovic/home/home_test.gno b/examples_drafts/r/matijamarjanovic/home/home_test.gno similarity index 100% rename from examples/gno.land/r/matijamarjanovic/home/home_test.gno rename to examples_drafts/r/matijamarjanovic/home/home_test.gno diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/errors.gno b/examples_drafts/r/matijamarjanovic/tokenhub/errors.gno similarity index 100% rename from examples/gno.land/r/matijamarjanovic/tokenhub/errors.gno rename to examples_drafts/r/matijamarjanovic/tokenhub/errors.gno diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno b/examples_drafts/r/matijamarjanovic/tokenhub/getters.gno similarity index 100% rename from examples/gno.land/r/matijamarjanovic/tokenhub/getters.gno rename to examples_drafts/r/matijamarjanovic/tokenhub/getters.gno diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/gno.mod b/examples_drafts/r/matijamarjanovic/tokenhub/gno.mod similarity index 100% rename from examples/gno.land/r/matijamarjanovic/tokenhub/gno.mod rename to examples_drafts/r/matijamarjanovic/tokenhub/gno.mod diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/render.gno b/examples_drafts/r/matijamarjanovic/tokenhub/render.gno similarity index 100% rename from examples/gno.land/r/matijamarjanovic/tokenhub/render.gno rename to examples_drafts/r/matijamarjanovic/tokenhub/render.gno diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples_drafts/r/matijamarjanovic/tokenhub/tokenhub.gno similarity index 100% rename from examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno rename to examples_drafts/r/matijamarjanovic/tokenhub/tokenhub.gno diff --git a/examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub_test.gno b/examples_drafts/r/matijamarjanovic/tokenhub/tokenhub_test.gno similarity index 100% rename from examples/gno.land/r/matijamarjanovic/tokenhub/tokenhub_test.gno rename to examples_drafts/r/matijamarjanovic/tokenhub/tokenhub_test.gno diff --git a/examples/gno.land/r/moul/home/gno.mod b/examples_drafts/r/moul/home/gno.mod similarity index 100% rename from examples/gno.land/r/moul/home/gno.mod rename to examples_drafts/r/moul/home/gno.mod diff --git a/examples/gno.land/r/moul/home/home.gno b/examples_drafts/r/moul/home/home.gno similarity index 100% rename from examples/gno.land/r/moul/home/home.gno rename to examples_drafts/r/moul/home/home.gno diff --git a/examples/gno.land/r/moul/home/z1_filetest.gno b/examples_drafts/r/moul/home/z1_filetest.gno similarity index 100% rename from examples/gno.land/r/moul/home/z1_filetest.gno rename to examples_drafts/r/moul/home/z1_filetest.gno diff --git a/examples/gno.land/r/moul/home/z2_filetest.gno b/examples_drafts/r/moul/home/z2_filetest.gno similarity index 100% rename from examples/gno.land/r/moul/home/z2_filetest.gno rename to examples_drafts/r/moul/home/z2_filetest.gno diff --git a/examples/gno.land/r/moul/present/gno.mod b/examples_drafts/r/moul/present/gno.mod similarity index 100% rename from examples/gno.land/r/moul/present/gno.mod rename to examples_drafts/r/moul/present/gno.mod diff --git a/examples/gno.land/r/moul/present/present.gno b/examples_drafts/r/moul/present/present.gno similarity index 100% rename from examples/gno.land/r/moul/present/present.gno rename to examples_drafts/r/moul/present/present.gno diff --git a/examples/gno.land/r/moul/present/present_filetest.gno b/examples_drafts/r/moul/present/present_filetest.gno similarity index 100% rename from examples/gno.land/r/moul/present/present_filetest.gno rename to examples_drafts/r/moul/present/present_filetest.gno diff --git a/examples/gno.land/r/moul/present/present_init.gno b/examples_drafts/r/moul/present/present_init.gno similarity index 100% rename from examples/gno.land/r/moul/present/present_init.gno rename to examples_drafts/r/moul/present/present_init.gno diff --git a/examples/gno.land/r/mouss/config/config.gno b/examples_drafts/r/mouss/config/config.gno similarity index 100% rename from examples/gno.land/r/mouss/config/config.gno rename to examples_drafts/r/mouss/config/config.gno diff --git a/examples/gno.land/r/mouss/config/config_test.gno b/examples_drafts/r/mouss/config/config_test.gno similarity index 100% rename from examples/gno.land/r/mouss/config/config_test.gno rename to examples_drafts/r/mouss/config/config_test.gno diff --git a/examples/gno.land/r/mouss/config/gno.mod b/examples_drafts/r/mouss/config/gno.mod similarity index 100% rename from examples/gno.land/r/mouss/config/gno.mod rename to examples_drafts/r/mouss/config/gno.mod diff --git a/examples/gno.land/r/mouss/home/gno.mod b/examples_drafts/r/mouss/home/gno.mod similarity index 100% rename from examples/gno.land/r/mouss/home/gno.mod rename to examples_drafts/r/mouss/home/gno.mod diff --git a/examples/gno.land/r/mouss/home/home.gno b/examples_drafts/r/mouss/home/home.gno similarity index 100% rename from examples/gno.land/r/mouss/home/home.gno rename to examples_drafts/r/mouss/home/home.gno diff --git a/examples/gno.land/r/mouss/home/home_test.gno b/examples_drafts/r/mouss/home/home_test.gno similarity index 100% rename from examples/gno.land/r/mouss/home/home_test.gno rename to examples_drafts/r/mouss/home/home_test.gno diff --git a/examples/gno.land/r/stefann/fomo3d/errors.gno b/examples_drafts/r/stefann/fomo3d/errors.gno similarity index 100% rename from examples/gno.land/r/stefann/fomo3d/errors.gno rename to examples_drafts/r/stefann/fomo3d/errors.gno diff --git a/examples/gno.land/r/stefann/fomo3d/events.gno b/examples_drafts/r/stefann/fomo3d/events.gno similarity index 100% rename from examples/gno.land/r/stefann/fomo3d/events.gno rename to examples_drafts/r/stefann/fomo3d/events.gno diff --git a/examples/gno.land/r/stefann/fomo3d/fomo3d.gno b/examples_drafts/r/stefann/fomo3d/fomo3d.gno similarity index 100% rename from examples/gno.land/r/stefann/fomo3d/fomo3d.gno rename to examples_drafts/r/stefann/fomo3d/fomo3d.gno diff --git a/examples/gno.land/r/stefann/fomo3d/fomo3d_test.gno b/examples_drafts/r/stefann/fomo3d/fomo3d_test.gno similarity index 100% rename from examples/gno.land/r/stefann/fomo3d/fomo3d_test.gno rename to examples_drafts/r/stefann/fomo3d/fomo3d_test.gno diff --git a/examples/gno.land/r/stefann/fomo3d/gno.mod b/examples_drafts/r/stefann/fomo3d/gno.mod similarity index 100% rename from examples/gno.land/r/stefann/fomo3d/gno.mod rename to examples_drafts/r/stefann/fomo3d/gno.mod diff --git a/examples/gno.land/r/stefann/fomo3d/nft.gno b/examples_drafts/r/stefann/fomo3d/nft.gno similarity index 100% rename from examples/gno.land/r/stefann/fomo3d/nft.gno rename to examples_drafts/r/stefann/fomo3d/nft.gno diff --git a/examples/gno.land/r/stefann/fomo3d/render.gno b/examples_drafts/r/stefann/fomo3d/render.gno similarity index 100% rename from examples/gno.land/r/stefann/fomo3d/render.gno rename to examples_drafts/r/stefann/fomo3d/render.gno diff --git a/examples/gno.land/r/stefann/home/gno.mod b/examples_drafts/r/stefann/home/gno.mod similarity index 100% rename from examples/gno.land/r/stefann/home/gno.mod rename to examples_drafts/r/stefann/home/gno.mod diff --git a/examples/gno.land/r/stefann/home/home.gno b/examples_drafts/r/stefann/home/home.gno similarity index 100% rename from examples/gno.land/r/stefann/home/home.gno rename to examples_drafts/r/stefann/home/home.gno diff --git a/examples/gno.land/r/stefann/home/home_test.gno b/examples_drafts/r/stefann/home/home_test.gno similarity index 100% rename from examples/gno.land/r/stefann/home/home_test.gno rename to examples_drafts/r/stefann/home/home_test.gno diff --git a/examples/gno.land/r/ursulovic/home/gno.mod b/examples_drafts/r/ursulovic/home/gno.mod similarity index 100% rename from examples/gno.land/r/ursulovic/home/gno.mod rename to examples_drafts/r/ursulovic/home/gno.mod diff --git a/examples/gno.land/r/ursulovic/home/home.gno b/examples_drafts/r/ursulovic/home/home.gno similarity index 100% rename from examples/gno.land/r/ursulovic/home/home.gno rename to examples_drafts/r/ursulovic/home/home.gno diff --git a/examples/gno.land/r/ursulovic/home/home_test.gno b/examples_drafts/r/ursulovic/home/home_test.gno similarity index 100% rename from examples/gno.land/r/ursulovic/home/home_test.gno rename to examples_drafts/r/ursulovic/home/home_test.gno diff --git a/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_c/integration_filetest.gno b/examples_drafts/r/x/manfred_upgrade_patterns/upgrade_c/integration_filetest.gno similarity index 100% rename from examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_c/integration_filetest.gno rename to examples_drafts/r/x/manfred_upgrade_patterns/upgrade_c/integration_filetest.gno diff --git a/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_c/root/root.gno b/examples_drafts/r/x/manfred_upgrade_patterns/upgrade_c/root/root.gno similarity index 100% rename from examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_c/root/root.gno rename to examples_drafts/r/x/manfred_upgrade_patterns/upgrade_c/root/root.gno diff --git a/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_c/v1/v1.gno b/examples_drafts/r/x/manfred_upgrade_patterns/upgrade_c/v1/v1.gno similarity index 100% rename from examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_c/v1/v1.gno rename to examples_drafts/r/x/manfred_upgrade_patterns/upgrade_c/v1/v1.gno diff --git a/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_c/v2/v2.gno b/examples_drafts/r/x/manfred_upgrade_patterns/upgrade_c/v2/v2.gno similarity index 100% rename from examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_c/v2/v2.gno rename to examples_drafts/r/x/manfred_upgrade_patterns/upgrade_c/v2/v2.gno diff --git a/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_e/home.gno b/examples_drafts/r/x/manfred_upgrade_patterns/upgrade_e/home.gno similarity index 100% rename from examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_e/home.gno rename to examples_drafts/r/x/manfred_upgrade_patterns/upgrade_e/home.gno diff --git a/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_e/v1impl/v1impl.gno b/examples_drafts/r/x/manfred_upgrade_patterns/upgrade_e/v1impl/v1impl.gno similarity index 100% rename from examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_e/v1impl/v1impl.gno rename to examples_drafts/r/x/manfred_upgrade_patterns/upgrade_e/v1impl/v1impl.gno diff --git a/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_e/v1impl/z_filetest.gno b/examples_drafts/r/x/manfred_upgrade_patterns/upgrade_e/v1impl/z_filetest.gno similarity index 100% rename from examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_e/v1impl/z_filetest.gno rename to examples_drafts/r/x/manfred_upgrade_patterns/upgrade_e/v1impl/z_filetest.gno diff --git a/gno.land/cmd/gnoland/start_test.go b/gno.land/cmd/gnoland/start_test.go index 2f620fcd360..0860f3427e6 100644 --- a/gno.land/cmd/gnoland/start_test.go +++ b/gno.land/cmd/gnoland/start_test.go @@ -26,7 +26,6 @@ func retryUntilTimeout(ctx context.Context, cb func() bool) error { select { case <-ctx.Done(): ch <- ctx.Err() - return default: retry := cb() @@ -122,23 +121,24 @@ func TestStart_Lazy(t *testing.T) { // Set up the command ctx g, gCtx := errgroup.WithContext(ctx) + // Set up the retry ctx + rCtx, rCtxCancelFn := context.WithTimeout(gCtx, 5*time.Second) + defer rCtxCancelFn() + // Start the node g.Go(func() error { - return newRootCmd(io).ParseAndRun(gCtx, args) + err := newRootCmd(io).ParseAndRun(rCtx, args) + return err }) - // Set up the retry ctx - retryCtx, retryCtxCancelFn := context.WithTimeout(ctx, 5*time.Second) - defer retryCtxCancelFn() - // This is a very janky way to verify the node has started. // The alternative is to poll the node's RPC endpoints, but for some reason // this introduces a lot of flakyness to the testing suite -- shocking! // In an effort to keep this simple, and avoid randomly failing tests, // we query the CLI output of the command - require.NoError(t, retryUntilTimeout(retryCtx, func() bool { + require.NoError(t, retryUntilTimeout(rCtx, func() bool { return !strings.Contains(mockOut.String(), startGraphic) - })) + }), g.Wait()) cancelFn() // stop the node require.NoError(t, g.Wait()) diff --git a/gno.land/pkg/gnoland/genesis.go b/gno.land/pkg/gnoland/genesis.go index d273687dcf3..8633a80e999 100644 --- a/gno.land/pkg/gnoland/genesis.go +++ b/gno.land/pkg/gnoland/genesis.go @@ -177,7 +177,7 @@ func LoadPackagesFromDir(dir string, creator bft.Address, fee std.Fee) ([]TxWith // list all packages from target path pkgs, err := gnomod.ListPkgs(dir) if err != nil { - return nil, fmt.Errorf("listing gno packages: %w", err) + return nil, fmt.Errorf("listing gno packages from gnomod: %w", err) } // Sort packages by dependencies. diff --git a/gno.land/pkg/integration/pkgloader.go b/gno.land/pkg/integration/pkgloader.go index 5968ce2b4ef..3074a1abc8e 100644 --- a/gno.land/pkg/integration/pkgloader.go +++ b/gno.land/pkg/integration/pkgloader.go @@ -88,7 +88,7 @@ func (pl *PkgsLoader) LoadAllPackagesFromDir(path string) error { // list all packages from target path pkgslist, err := gnomod.ListPkgs(path) if err != nil { - return fmt.Errorf("listing gno packages: %w", err) + return fmt.Errorf("listing gno packages from gnomod: %w", err) } for _, pkg := range pkgslist { diff --git a/gno.land/pkg/integration/testdata/addpkg_namespace.txtar b/gno.land/pkg/integration/testdata/addpkg_namespace.txtar index f66f51ade46..efdf93b8664 100644 --- a/gno.land/pkg/integration/testdata/addpkg_namespace.txtar +++ b/gno.land/pkg/integration/testdata/addpkg_namespace.txtar @@ -9,10 +9,9 @@ patchpkg "g1manfred47kzduec920z88wfr64ylksmdcedlf5" $admin_user_addr # use our c gnoland start # Check if sys/names is disabled -# gui call -> sys/names.IsEnabled -gnokey maketx call -pkgpath gno.land/r/sys/names -func IsEnabled -gas-fee 100000ugnot -gas-wanted 200000 -broadcast -chainid tendermint_test gui -stdout 'OK!' -stdout 'false' +# qeval -> sys/names.IsEnabled +gnokey query vm/qeval --data "gno.land/r/sys/names.IsEnabled()" +stdout 'false bool' # Gui should be able to addpkg on test1 addr # gui addpkg -> gno.land/r//mysuperpkg @@ -30,10 +29,10 @@ gnokey maketx call -pkgpath gno.land/r/sys/names -func Enable -gas-fee 100000ugn stdout 'OK!' # Check that `sys/names` has been enabled -# gui call -> sys/names.IsEnabled -gnokey maketx call -pkgpath gno.land/r/sys/names -func IsEnabled -gas-fee 100000ugnot -gas-wanted 200000 -broadcast -chainid tendermint_test gui -stdout 'OK!' -stdout 'true' +# qeval -> sys/names.IsEnabled +gnokey query vm/qeval --data "gno.land/r/sys/names.IsEnabled()" +stdout 'true bool' + # Try to add a pkg an with unregistered user # gui addpkg -> gno.land/r//one diff --git a/gno.land/pkg/integration/testdata/govdao_proposal_add_member.txtar b/gno.land/pkg/integration/testdata/govdao_proposal_add_member.txtar new file mode 100644 index 00000000000..680251439d1 --- /dev/null +++ b/gno.land/pkg/integration/testdata/govdao_proposal_add_member.txtar @@ -0,0 +1,78 @@ +adduserfrom member 'success myself purchase tray reject demise scene little legend someone lunar hope media goat regular test area smart save flee surround attack rapid smoke' +stdout 'g1c0j899h88nwyvnzvh5jagpq6fkkyuj76nld6t0' + +adduserfrom newmember 'smooth crawl poverty trumpet glare useful curtain annual pluck lunar example merge ready forum better verb rescue rule mechanic dynamic drift bench release weekend' +stdout 'g1rfznvu6qfa0sc76cplk5wpqexvefqccjunady0' + +loadpkg gno.land/r/gov/dao +loadpkg gno.land/r/gov/dao/v3/impl + +# load specific govDAO implementation and needed users for your integration test +loadpkg gno.land/r/gov/dao/v3/loader $WORK/loader + +gnoland start + +# call gov/dao render to check everything is working as expected and the loader worked +gnokey maketx call -pkgpath gno.land/r/gov/dao -func Render -gas-fee 1000000ugnot -gas-wanted 10000000 -args '' -broadcast -chainid=tendermint_test test1 +stdout OK! + +# add the proposal +gnokey maketx addpkg -pkgdir $WORK/proposer -pkgpath gno.land/r/gov/dao/v3/proposer -gas-fee 1000000ugnot -gas-wanted 100000000 -broadcast -chainid=tendermint_test member +stdout OK! + +# call gov/dao render to check everything the proposal was created +gnokey maketx call -pkgpath gno.land/r/gov/dao -func Render -gas-fee 1000000ugnot -gas-wanted 10000000 -args '0' -broadcast -chainid=tendermint_test test1 +stdout 'this is my portfolio' +stdout OK! + +# vote on the proposal +gnokey maketx call -pkgpath gno.land/r/gov/dao -func MustVoteOnProposalSimple -gas-fee 1000000ugnot -gas-wanted 10000000 -args 0 -args YES -broadcast -chainid=tendermint_test member +stdout OK! + +# call proposal execution +gnokey maketx call -pkgpath gno.land/r/gov/dao -func ExecuteProposal -gas-fee 1000000ugnot -gas-wanted 10000000 -args 0 -broadcast -chainid=tendermint_test member +stdout OK! + +# check output +gnokey maketx call -pkgpath gno.land/r/gov/dao/v3/memberstore -func Render -gas-fee 1000000ugnot -gas-wanted 10000000 -args '' -broadcast -chainid=tendermint_test test1 +stdout 'Tier T2 contains 1 members' +stdout OK! + +-- proposer/create_proposal.gno -- +package create_proposal + +import ( + "std" + + "gno.land/r/gov/dao" + "gno.land/r/gov/dao/v3/impl" +) + +func init() { + preq := impl.NewAddMemberRequest(std.Address("g1rfznvu6qfa0sc76cplk5wpqexvefqccjunady0"), "T2", "this is my portfolio") + dao.MustCreateProposal(preq) +} + +-- loader/load_govdao.gno -- +package load_govdao + +import ( + "std" + + "gno.land/r/gov/dao" + "gno.land/r/gov/dao/v3/impl" + "gno.land/r/gov/dao/v3/memberstore" +) + +func init() { + memberstore.Get().SetTier(memberstore.T1) + memberstore.Get().SetTier(memberstore.T2) + memberstore.Get().SetTier(memberstore.T3) + + memberstore.Get().SetMember(memberstore.T1, std.Address("g1c0j899h88nwyvnzvh5jagpq6fkkyuj76nld6t0"), &memberstore.Member{InvitationPoints: 3}) // member address + + dao.UpdateImpl(dao.UpdateRequest{ + DAO: impl.GetInstance(), + AllowedDAOs: []string{"gno.land/r/gov/dao/v3/impl"}, + }) +} diff --git a/gno.land/pkg/integration/testdata/govdao_proposal_change_law.txtar b/gno.land/pkg/integration/testdata/govdao_proposal_change_law.txtar new file mode 100644 index 00000000000..237a9fdf377 --- /dev/null +++ b/gno.land/pkg/integration/testdata/govdao_proposal_change_law.txtar @@ -0,0 +1,68 @@ +adduserfrom member 'success myself purchase tray reject demise scene little legend someone lunar hope media goat regular test area smart save flee surround attack rapid smoke' +stdout 'g1c0j899h88nwyvnzvh5jagpq6fkkyuj76nld6t0' + +loadpkg gno.land/r/gov/dao +loadpkg gno.land/r/gov/dao/v3/impl + +# load specific govDAO implementation and needed users for your integration test +loadpkg gno.land/r/gov/dao/v3/loader $WORK/loader + +gnoland start + +# call gov/dao render to check everything is working as expected and the loader worked +gnokey maketx call -pkgpath gno.land/r/gov/dao -func Render -gas-fee 1000000ugnot -gas-wanted 10000000 -args '' -broadcast -chainid=tendermint_test test1 +stdout OK! + +# add the proposal +gnokey maketx addpkg -pkgdir $WORK/proposer -pkgpath gno.land/r/gov/dao/v3/proposer -gas-fee 1000000ugnot -gas-wanted 100000000 -broadcast -chainid=tendermint_test member +stdout OK! + +# call gov/dao render to check everything the proposal was created +gnokey maketx call -pkgpath gno.land/r/gov/dao -func Render -gas-fee 1000000ugnot -gas-wanted 10000000 -args '0' -broadcast -chainid=tendermint_test test1 +stdout '- Supermajority: 50%' +stdout OK! + +# vote on the proposal +gnokey maketx call -pkgpath gno.land/r/gov/dao -func MustVoteOnProposalSimple -gas-fee 1000000ugnot -gas-wanted 10000000 -args 0 -args YES -broadcast -chainid=tendermint_test member +stdout OK! + +# call proposal execution +gnokey maketx call -pkgpath gno.land/r/gov/dao -func ExecuteProposal -gas-fee 1000000ugnot -gas-wanted 10000000 -args 0 -broadcast -chainid=tendermint_test member +stdout OK! + +-- proposer/create_proposal.gno -- +package create_proposal + +import ( + "gno.land/r/gov/dao" + "gno.land/r/gov/dao/v3/impl" +) + +func init() { + preq := impl.NewChangeLawRequest(&impl.Law{Supermajority:50}) + dao.MustCreateProposal(preq) +} + +-- loader/load_govdao.gno -- +package load_govdao + +import ( + "std" + + "gno.land/r/gov/dao" + "gno.land/r/gov/dao/v3/impl" + "gno.land/r/gov/dao/v3/memberstore" +) + +func init() { + memberstore.Get().SetTier(memberstore.T1) + memberstore.Get().SetTier(memberstore.T2) + memberstore.Get().SetTier(memberstore.T3) + + memberstore.Get().SetMember(memberstore.T1, std.Address("g1c0j899h88nwyvnzvh5jagpq6fkkyuj76nld6t0"), &memberstore.Member{InvitationPoints: 3}) // member address + + dao.UpdateImpl(dao.UpdateRequest{ + DAO: impl.GetInstance(), + AllowedDAOs: []string{"gno.land/r/gov/dao/v3/impl"}, + }) +} diff --git a/gno.land/pkg/integration/testdata/govdao_proposal_new_param.txtar b/gno.land/pkg/integration/testdata/govdao_proposal_new_param.txtar new file mode 100644 index 00000000000..eaee5c0d444 --- /dev/null +++ b/gno.land/pkg/integration/testdata/govdao_proposal_new_param.txtar @@ -0,0 +1,72 @@ +adduserfrom member 'success myself purchase tray reject demise scene little legend someone lunar hope media goat regular test area smart save flee surround attack rapid smoke' +stdout 'g1c0j899h88nwyvnzvh5jagpq6fkkyuj76nld6t0' + +loadpkg gno.land/r/gov/dao +loadpkg gno.land/r/gov/dao/v3/impl +loadpkg gno.land/r/sys/params + +# load specific govDAO implementation and needed users for your integration test +loadpkg gno.land/r/gov/dao/v3/loader $WORK/loader + +gnoland start + +# call gov/dao render to check everything is working as expected and the loader worked +gnokey maketx call -pkgpath gno.land/r/gov/dao -func Render -gas-fee 1000000ugnot -gas-wanted 10000000 -args '' -broadcast -chainid=tendermint_test test1 +stdout OK! + +# add the proposal +gnokey maketx addpkg -pkgdir $WORK/proposer -pkgpath gno.land/r/gov/dao/v3/proposer -gas-fee 1000000ugnot -gas-wanted 100000000 -broadcast -chainid=tendermint_test member +stdout OK! + +# call gov/dao render to check everything the proposal was created +gnokey maketx call -pkgpath gno.land/r/gov/dao -func Render -gas-fee 1000000ugnot -gas-wanted 10000000 -args '0' -broadcast -chainid=tendermint_test test1 +stdout 'This proposal wants to add a new key to sys/params: vm:bar:baz' +stdout OK! + +# vote on the proposal +gnokey maketx call -pkgpath gno.land/r/gov/dao -func MustVoteOnProposalSimple -gas-fee 1000000ugnot -gas-wanted 10000000 -args 0 -args YES -broadcast -chainid=tendermint_test member +stdout OK! + +# call proposal execution +gnokey maketx call -pkgpath gno.land/r/gov/dao -func ExecuteProposal -gas-fee 1000000ugnot -gas-wanted 10000000 -args 0 -broadcast -chainid=tendermint_test member +stdout OK! + +# check output: +# no way to check output + +-- proposer/create_proposal.gno -- +package create_proposal + +import ( + "gno.land/r/gov/dao" + "gno.land/r/sys/params" +) + +func init() { + preq := params.NewSysParamStringPropRequest("vm", "bar", "baz", "qux") + dao.MustCreateProposal(preq) +} + +-- loader/load_govdao.gno -- +package load_govdao + +import ( + "std" + + "gno.land/r/gov/dao" + "gno.land/r/gov/dao/v3/impl" + "gno.land/r/gov/dao/v3/memberstore" +) + +func init() { + memberstore.Get().SetTier(memberstore.T1) + memberstore.Get().SetTier(memberstore.T2) + memberstore.Get().SetTier(memberstore.T3) + + memberstore.Get().SetMember(memberstore.T1, std.Address("g1c0j899h88nwyvnzvh5jagpq6fkkyuj76nld6t0"), &memberstore.Member{InvitationPoints: 3}) // member address + + dao.UpdateImpl(dao.UpdateRequest{ + DAO: impl.GetInstance(), + AllowedDAOs: []string{"gno.land/r/gov/dao/v3/impl"}, + }) +} diff --git a/gno.land/pkg/integration/testdata/govdao_proposal_new_post.txtar b/gno.land/pkg/integration/testdata/govdao_proposal_new_post.txtar new file mode 100644 index 00000000000..9374c543eb8 --- /dev/null +++ b/gno.land/pkg/integration/testdata/govdao_proposal_new_post.txtar @@ -0,0 +1,77 @@ +adduserfrom member 'success myself purchase tray reject demise scene little legend someone lunar hope media goat regular test area smart save flee surround attack rapid smoke' +stdout 'g1c0j899h88nwyvnzvh5jagpq6fkkyuj76nld6t0' + +loadpkg gno.land/r/gov/dao +loadpkg gno.land/r/gov/dao/v3/impl +loadpkg gno.land/r/gnoland/blog + +# load specific govDAO implementation and needed users for your integration test +loadpkg gno.land/r/gov/dao/v3/loader $WORK/loader + +gnoland start + +# call gov/dao render to check everything is working as expected and the loader worked +gnokey maketx call -pkgpath gno.land/r/gov/dao -func Render -gas-fee 1000000ugnot -gas-wanted 10000000 -args '' -broadcast -chainid=tendermint_test test1 +stdout OK! + +# add the proposal +gnokey maketx addpkg -pkgdir $WORK/proposer -pkgpath gno.land/r/gov/dao/v3/proposer -gas-fee 1000000ugnot -gas-wanted 100000000 -broadcast -chainid=tendermint_test member +stdout OK! + +# call gov/dao render to check everything the proposal was created +gnokey maketx call -pkgpath gno.land/r/gov/dao -func Render -gas-fee 1000000ugnot -gas-wanted 10000000 -args '0' -broadcast -chainid=tendermint_test test1 +stdout '- Post Title: title' +stdout '- Post Publication Date: 1987-06-24T18:25:43.511Z' +stdout '- Authors: author' +stdout '- Tags: tag1, tag2' +stdout OK! + +# vote on the proposal +gnokey maketx call -pkgpath gno.land/r/gov/dao -func MustVoteOnProposalSimple -gas-fee 1000000ugnot -gas-wanted 10000000 -args 0 -args YES -broadcast -chainid=tendermint_test member +stdout OK! + +# call proposal execution +gnokey maketx call -pkgpath gno.land/r/gov/dao -func ExecuteProposal -gas-fee 1000000ugnot -gas-wanted 10000000 -args 0 -broadcast -chainid=tendermint_test member +stdout OK! + +# check output +gnokey maketx call -pkgpath gno.land/r/gnoland/blog -func Render -gas-fee 1000000ugnot -gas-wanted 10000000 -args 'p/slug' -broadcast -chainid=tendermint_test test1 +stdout 'Written by author on 24 Jun 1987' +stdout OK! + +-- proposer/create_proposal.gno -- +package create_proposal + +import ( + "gno.land/r/gov/dao" + gnoblog "gno.land/r/gnoland/blog" +) + +func init() { + preq := gnoblog.NewPostProposalRequest("slug", "title", "body", "1987-06-24T18:25:43.511Z", "author", "tag1, tag2") + dao.MustCreateProposal(preq) +} + +-- loader/load_govdao.gno -- +package load_govdao + +import ( + "std" + + "gno.land/r/gov/dao" + "gno.land/r/gov/dao/v3/impl" + "gno.land/r/gov/dao/v3/memberstore" +) + +func init() { + memberstore.Get().SetTier(memberstore.T1) + memberstore.Get().SetTier(memberstore.T2) + memberstore.Get().SetTier(memberstore.T3) + + memberstore.Get().SetMember(memberstore.T1, std.Address("g1c0j899h88nwyvnzvh5jagpq6fkkyuj76nld6t0"), &memberstore.Member{InvitationPoints: 3}) // member address + + dao.UpdateImpl(dao.UpdateRequest{ + DAO: impl.GetInstance(), + AllowedDAOs: []string{"gno.land/r/gov/dao/v3/impl"}, + }) +} diff --git a/gno.land/pkg/integration/testdata/govdao_proposal_withdraw_member.txtar b/gno.land/pkg/integration/testdata/govdao_proposal_withdraw_member.txtar new file mode 100644 index 00000000000..7b9e803d942 --- /dev/null +++ b/gno.land/pkg/integration/testdata/govdao_proposal_withdraw_member.txtar @@ -0,0 +1,84 @@ +adduserfrom member 'success myself purchase tray reject demise scene little legend someone lunar hope media goat regular test area smart save flee surround attack rapid smoke' +stdout 'g1c0j899h88nwyvnzvh5jagpq6fkkyuj76nld6t0' + +adduserfrom withdrawmember 'smooth crawl poverty trumpet glare useful curtain annual pluck lunar example merge ready forum better verb rescue rule mechanic dynamic drift bench release weekend' +stdout 'g1rfznvu6qfa0sc76cplk5wpqexvefqccjunady0' + +loadpkg gno.land/r/gov/dao +loadpkg gno.land/r/gov/dao/v3/impl + +# load specific govDAO implementation and needed users for your integration test +loadpkg gno.land/r/gov/dao/v3/loader $WORK/loader + +gnoland start + +# call gov/dao render to check everything is working as expected and the loader worked +gnokey maketx call -pkgpath gno.land/r/gov/dao -func Render -gas-fee 1000000ugnot -gas-wanted 10000000 -args '' -broadcast -chainid=tendermint_test test1 +stdout OK! + +# add the proposal +gnokey maketx addpkg -pkgdir $WORK/proposer -pkgpath gno.land/r/gov/dao/v3/proposer -gas-fee 1000000ugnot -gas-wanted 100000000 -broadcast -chainid=tendermint_test member +stdout OK! + +# call gov/dao render to check everything the proposal was created +gnokey maketx call -pkgpath gno.land/r/gov/dao -func Render -gas-fee 1000000ugnot -gas-wanted 10000000 -args '0' -broadcast -chainid=tendermint_test test1 +stdout 'not responding' +stdout OK! + +# vote on the proposal 1 +gnokey maketx call -pkgpath gno.land/r/gov/dao -func MustVoteOnProposalSimple -gas-fee 1000000ugnot -gas-wanted 10000000 -args 0 -args YES -broadcast -chainid=tendermint_test member +stdout OK! + +# vote on the proposal 2 +gnokey maketx call -pkgpath gno.land/r/gov/dao -func MustVoteOnProposalSimple -gas-fee 1000000ugnot -gas-wanted 10000000 -args 0 -args YES -broadcast -chainid=tendermint_test withdrawmember +stdout OK! + +# call proposal execution +gnokey maketx call -pkgpath gno.land/r/gov/dao -func ExecuteProposal -gas-fee 1000000ugnot -gas-wanted 10000000 -args 0 -broadcast -chainid=tendermint_test member +stdout OK! + +# check output +gnokey maketx call -pkgpath gno.land/r/gov/dao/v3/memberstore -func Render -gas-fee 1000000ugnot -gas-wanted 10000000 -args '' -broadcast -chainid=tendermint_test test1 +stdout 'Tier T2 contains 0 members' +stdout OK! + +-- proposer/create_proposal.gno -- +package create_proposal + +import ( + "std" + + "gno.land/r/gov/dao" + "gno.land/r/gov/dao/v3/impl" +) + +func init() { + + preq := impl.NewWithdrawMemberRequest(std.Address("g1rfznvu6qfa0sc76cplk5wpqexvefqccjunady0"), "not responding") + dao.MustCreateProposal(preq) +} + +-- loader/load_govdao.gno -- +package load_govdao + +import ( + "std" + + "gno.land/r/gov/dao" + "gno.land/r/gov/dao/v3/impl" + "gno.land/r/gov/dao/v3/memberstore" +) + +func init() { + memberstore.Get().SetTier(memberstore.T1) + memberstore.Get().SetTier(memberstore.T2) + memberstore.Get().SetTier(memberstore.T3) + + memberstore.Get().SetMember(memberstore.T1, std.Address("g1c0j899h88nwyvnzvh5jagpq6fkkyuj76nld6t0"), &memberstore.Member{InvitationPoints: 3}) // member address + memberstore.Get().SetMember(memberstore.T2, std.Address("g1rfznvu6qfa0sc76cplk5wpqexvefqccjunady0"), &memberstore.Member{InvitationPoints: 2}) // member to withdraw + + dao.UpdateImpl(dao.UpdateRequest{ + DAO: impl.GetInstance(), + AllowedDAOs: []string{"gno.land/r/gov/dao/v3/impl"}, + }) +} diff --git a/gno.land/pkg/integration/testdata/wugnot.txtar b/gno.land/pkg/integration/testdata/wugnot.txtar index d06c31e7fd8..dde03ae3f02 100644 --- a/gno.land/pkg/integration/testdata/wugnot.txtar +++ b/gno.land/pkg/integration/testdata/wugnot.txtar @@ -1,40 +1,68 @@ loadpkg gno.land/r/demo/wugnot +adduser user1 +adduser user2 +adduser user3 + gnoland start +gnokey query auth/accounts/$user1_user_addr +gnokey query auth/accounts/$user2_user_addr +gnokey query auth/accounts/$user3_user_addr + gnokey query vm/qrender --data "gno.land/r/demo/wugnot:" stdout '# wrapped GNOT \(\$wugnot\)' stdout 'Decimals..: 0' stdout 'Total supply..: 0' stdout 'Known accounts..: 0' -gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func Deposit -send 12345678ugnot -gas-fee 1000000ugnot -gas-wanted 50000000 -broadcast -chainid=tendermint_test test1 +# user1 balance should be empty +gnokey query vm/qeval --data "gno.land/r/demo/wugnot.BalanceOf(\"${user1_user_addr}\")" +stdout '0 uint64' + +# Deposit using user1 +gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func Deposit -send 10000ugnot -gas-fee 100000ugnot -gas-wanted 50000000 -broadcast -chainid=tendermint_test user1 stdout 'OK!' +gnokey query vm/qeval --data "gno.land/r/demo/wugnot.BalanceOf(\"${user1_user_addr}\")" +stdout '10000 uint64' + gnokey query vm/qrender --data "gno.land/r/demo/wugnot:" -stdout 'Total supply..: 12345678' +stdout 'Total supply..: 10000' stdout 'Known accounts..: 1' -# XXX: use test2 instead (depends on https://github.com/gnolang/gno/issues/1269#issuecomment-1806386069) -gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func Deposit -send 12345678ugnot -gas-fee 1000000ugnot -gas-wanted 100000000 -broadcast -chainid=tendermint_test test1 + +# user2 balance should be empty +gnokey query vm/qeval --data "gno.land/r/demo/wugnot.BalanceOf(\"${user2_user_addr}\")" +stdout '0 uint64' + +# Deposit using user2 +gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func Deposit -send 10000ugnot -gas-fee 10000ugnot -gas-wanted 10_000_000 -broadcast -chainid=tendermint_test user2 stdout 'OK!' +gnokey query vm/qeval --data "gno.land/r/demo/wugnot.BalanceOf(\"${user1_user_addr}\")" +stdout '10000 uint64' + +gnokey query vm/qeval --data "gno.land/r/demo/wugnot.BalanceOf(\"${user2_user_addr}\")" +stdout '10000 uint64' + +## We should have 2 accounts gnokey query vm/qrender --data "gno.land/r/demo/wugnot:" -stdout 'Total supply..: 24691356' -stdout 'Known accounts..: 1' # should be 2 once we can use test2 +stdout 'Total supply..: 20000' +stdout 'Known accounts..: 2' + -# XXX: replace hardcoded address with test3 -gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func Transfer -gas-fee 1000000ugnot -gas-wanted 100000000 -args 'g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq' -args '10000000' -broadcast -chainid=tendermint_test test1 +gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func Transfer -gas-fee 10000ugnot -gas-wanted 10_000_000 -args ${user3_user_addr} -args '10000' -broadcast -chainid=tendermint_test user1 stdout 'OK!' gnokey query vm/qrender --data "gno.land/r/demo/wugnot:" -stdout 'Total supply..: 24691356' -stdout 'Known accounts..: 2' # should be 3 once we can use test2 +stdout 'Total supply..: 20000' +stdout 'Known accounts..: 3' # XXX: use test3 instead (depends on https://github.com/gnolang/gno/issues/1269#issuecomment-1806386069) -gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func Withdraw -args 10000000 -gas-fee 1000000ugnot -gas-wanted 100000000 -broadcast -chainid=tendermint_test test1 +gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func Withdraw -args 10000 -gas-fee 10000ugnot -gas-wanted 10_000_000 -broadcast -chainid=tendermint_test user3 stdout 'OK!' gnokey query vm/qrender --data "gno.land/r/demo/wugnot:" -stdout 'Total supply..: 14691356' -stdout 'Known accounts..: 2' # should be 3 once we can use test2 +stdout 'Total supply..: 10000' +stdout 'Known accounts..: 3' diff --git a/gno.land/pkg/integration/testscript_gnoland.go b/gno.land/pkg/integration/testscript_gnoland.go index 21e3f88de39..10e606fd428 100644 --- a/gno.land/pkg/integration/testscript_gnoland.go +++ b/gno.land/pkg/integration/testscript_gnoland.go @@ -705,7 +705,7 @@ func createAccountFrom(ts *testscript.TestScript, kb keys.Keybase, accountName, return gnoland.Balance{ Address: address, - Amount: std.Coins{std.NewCoin(ugnot.Denom, 10e6)}, + Amount: std.Coins{std.NewCoin(ugnot.Denom, 10e8)}, }, nil } diff --git a/gnovm/Makefile b/gnovm/Makefile index 915bf294814..ab6ed2ee505 100644 --- a/gnovm/Makefile +++ b/gnovm/Makefile @@ -121,7 +121,8 @@ _test.stdlibs: go run ./cmd/gno test -v ./stdlibs/... .PHONY: _test.filetest -_test.filetest:; +_test.filetest: + @echo "use 'go test pkg/gnolang/files_test.go -test.short --update-golden-tests' to update filetest expectations" go test pkg/gnolang/files_test.go -test.short -run 'TestFiles$$/' $(GOTEST_FLAGS) ######################################## diff --git a/gnovm/cmd/gno/download_deps.go b/gnovm/cmd/gno/download_deps.go index 4e638eb4970..cf68b1abffc 100644 --- a/gnovm/cmd/gno/download_deps.go +++ b/gnovm/cmd/gno/download_deps.go @@ -16,7 +16,7 @@ import ( ) // downloadDeps recursively fetches the imports of a local package while following a given gno.mod replace directives -func downloadDeps(io commands.IO, pkgDir string, gnoMod *gnomod.File, fetcher pkgdownload.PackageFetcher) error { +func downloadDeps(io commands.IO, pkgDir string, gnoMod *gnomod.File, fetcher pkgdownload.PackageFetcher, visited map[string]struct{}) error { if fetcher == nil { return errors.New("fetcher is nil") } @@ -39,13 +39,20 @@ func downloadDeps(io commands.IO, pkgDir string, gnoMod *gnomod.File, fetcher pk continue } + // Cycle + redundancy check: Have we already started processing this dependency? + if _, exists := visited[resolvedPkgPath]; exists { + continue // Skip dependencies already being processed or finished in this run. + } + // Mark this dependency as visited *before* recursive call. + visited[resolvedPkgPath] = struct{}{} + depDir := gnomod.PackageDir("", module.Version{Path: resolvedPkgPath}) if err := downloadPackage(io, resolvedPkgPath, depDir, fetcher); err != nil { return fmt.Errorf("download import %q of %q: %w", resolvedPkgPath, pkgDir, err) } - if err := downloadDeps(io, depDir, gnoMod, fetcher); err != nil { + if err := downloadDeps(io, depDir, gnoMod, fetcher, visited); err != nil { return err } } diff --git a/gnovm/cmd/gno/download_deps_test.go b/gnovm/cmd/gno/download_deps_test.go index a0e3888def0..1f3393ab0e7 100644 --- a/gnovm/cmd/gno/download_deps_test.go +++ b/gnovm/cmd/gno/download_deps_test.go @@ -52,7 +52,7 @@ func TestDownloadDeps(t *testing.T) { "gno: downloading gno.land/p/demo/ufmt", }, }, { - desc: "fetch_gno.land/p/demo/blog6", + desc: "fetch_gno.land/p/demo/blog", pkgPath: "gno.land/p/demo/blog", modFile: gnomod.File{ Module: &modfile.Module{ @@ -61,7 +61,7 @@ func TestDownloadDeps(t *testing.T) { }, }, }, - requirements: []string{"avl", "blog", "diff", "uassert", "ufmt", "mux"}, + requirements: []string{"avl", "blog", "diff", "uassert", "ufmt", "mux", "nestedpkg", "testutils"}, ioErrContains: []string{ "gno: downloading gno.land/p/demo/blog", "gno: downloading gno.land/p/demo/avl", @@ -101,6 +101,7 @@ func TestDownloadDeps(t *testing.T) { }}, }, }, + // XXX: infinite loop (A imports B, B imports C, C imports A) } { t.Run(tc.desc, func(t *testing.T) { mockErr := bytes.NewBufferString("") @@ -118,7 +119,7 @@ func TestDownloadDeps(t *testing.T) { fetcher := examplespkgfetcher.New() // gno: downloading dependencies - err = downloadDeps(io, dirPath, &tc.modFile, fetcher) + err = downloadDeps(io, dirPath, &tc.modFile, fetcher, make(map[string]struct{})) if tc.errorShouldContain != "" { require.ErrorContains(t, err, tc.errorShouldContain) } else { @@ -144,7 +145,7 @@ func TestDownloadDeps(t *testing.T) { mockErr.Reset() // Try fetching again. Should be cached - downloadDeps(io, dirPath, &tc.modFile, fetcher) + err = downloadDeps(io, dirPath, &tc.modFile, fetcher, make(map[string]struct{})) for _, c := range tc.ioErrContains { assert.NotContains(t, mockErr.String(), c) } diff --git a/gnovm/cmd/gno/mod.go b/gnovm/cmd/gno/mod.go index b1604a86562..d007300576e 100644 --- a/gnovm/cmd/gno/mod.go +++ b/gnovm/cmd/gno/mod.go @@ -235,7 +235,7 @@ func execModDownload(cfg *modDownloadCfg, args []string, io commands.IO) error { return fmt.Errorf("validate: %w", err) } - if err := downloadDeps(io, path, gnoMod, fetcher); err != nil { + if err := downloadDeps(io, path, gnoMod, fetcher, make(map[string]struct{})); err != nil { return err } diff --git a/gnovm/pkg/gnolang/context.go b/gnovm/pkg/gnolang/context.go index 9fe72a06c66..c66879cdc2d 100644 --- a/gnovm/pkg/gnolang/context.go +++ b/gnovm/pkg/gnolang/context.go @@ -3,7 +3,7 @@ package gnolang type Stage string const ( - StagePre Stage = "StagePre" // e.g. preprocessing TODO use + StagePre Stage = "StagePre" // e.g. static evaluation during preprocessing StageAdd Stage = "StageAdd" // e.g. init() StageRun Stage = "StageRun" // e.g. main() ) diff --git a/gnovm/pkg/gnolang/frame.go b/gnovm/pkg/gnolang/frame.go index 78b213d0163..eb085bd4b8c 100644 --- a/gnovm/pkg/gnolang/frame.go +++ b/gnovm/pkg/gnolang/frame.go @@ -29,9 +29,10 @@ type Frame struct { LastPackage *PackageValue // previous frame's package LastRealm *Realm // previous frame's realm WithCross bool // true if called like cross(fn)(...). expects crossing() after. - DidCross bool // true if crossing() was called. + DidCrossing bool // true if crossing() was called. Defers []Defer // deferred calls IsDefer bool // was func defer called + IsRevive bool // calling revive() LastException *Exception // previous m.exception // test info @@ -52,7 +53,7 @@ func (fr Frame) String() string { fr.LastPackage.PkgPath, fr.LastRealm, fr.WithCross, - fr.DidCross, + fr.DidCrossing, fr.IsDefer, fr.LastException, ) @@ -91,11 +92,18 @@ func (fr *Frame) SetWithCross() { fr.WithCross = true } -func (fr *Frame) SetDidCross() { - if fr.DidCross { - panic("fr.DidCross already set") +func (fr *Frame) SetDidCrossing() { + if fr.DidCrossing { + panic("fr.DidCrossing already set") } - fr.DidCross = true + fr.DidCrossing = true +} + +func (fr *Frame) SetIsRevive() { + if fr.IsRevive { + panic("fr.IsRevive already set") + } + fr.IsRevive = true } //---------------------------------------- @@ -119,6 +127,10 @@ type Stacktrace struct { LastLine int } +func (s Stacktrace) IsZero() bool { + return s.Calls == nil && s.NumFramesElided == 0 && s.LastLine == 0 +} + func (s Stacktrace) String() string { var builder strings.Builder @@ -223,7 +235,13 @@ func toConstExpTrace(cte *ConstExpr) string { } } - return tv.V.String() + if tv.IsTypedNil() { + return "typed-nil" + } else if tv.IsUndefined() { + return "undefined" + } else { + return tv.V.String() + } } //---------------------------------------- diff --git a/gnovm/pkg/gnolang/gotypecheck.go b/gnovm/pkg/gnolang/gotypecheck.go index caa3219f603..66ba1bb1b90 100644 --- a/gnovm/pkg/gnolang/gotypecheck.go +++ b/gnovm/pkg/gnolang/gotypecheck.go @@ -2,7 +2,7 @@ package gnolang import ( "bytes" - "errors" + "fmt" "go/ast" "go/format" "go/parser" @@ -14,7 +14,6 @@ import ( "github.com/gnolang/gno/gnovm" "go.uber.org/multierr" - "golang.org/x/tools/go/ast/astutil" ) // type checking (using go/types) @@ -120,6 +119,7 @@ func (g *gnoImporter) parseCheckMemPackage(mpkg *gnovm.MemPackage, fmt_ bool) (* fset := token.NewFileSet() files := make([]*ast.File, 0, len(mpkg.Files)) + const parseOpts = parser.ParseComments | parser.DeclarationErrors | parser.SkipObjectResolution var errs error for _, file := range mpkg.Files { // Ignore non-gno files. @@ -131,7 +131,6 @@ func (g *gnoImporter) parseCheckMemPackage(mpkg *gnovm.MemPackage, fmt_ bool) (* continue } - const parseOpts = parser.ParseComments | parser.DeclarationErrors | parser.SkipObjectResolution f, err := parser.ParseFile(fset, path.Join(mpkg.Path, file.Name), file.Body, parseOpts) if err != nil { errs = multierr.Append(errs, err) @@ -160,11 +159,15 @@ func (g *gnoImporter) parseCheckMemPackage(mpkg *gnovm.MemPackage, fmt_ bool) (* //---------------------------------------- // Logical transforms - // filter crossings for type checker - if err := filterCrossing(f); err != nil { - errs = multierr.Append(errs, err) - continue - } + // No need to filter because of gnobuiltins.go. + // But keep this code block for future transforms. + /* + // filter crossings for type checker + if err := filterCrossing(f); err != nil { + errs = multierr.Append(errs, err) + continue + } + */ files = append(files, f) } @@ -172,6 +175,23 @@ func (g *gnoImporter) parseCheckMemPackage(mpkg *gnovm.MemPackage, fmt_ bool) (* return nil, errs } + // Add builtins file. + file := &gnovm.MemFile{ + Name: ".gnobuiltins.go", + Body: fmt.Sprintf(`package %s + +func istypednil(x any) bool { return false } // shim +func crossing() { } // shim +func cross[F any](fn F) F { return fn } // shim +func revive[F any](fn F) any { return nil } // shim +`, mpkg.Name), + } + f, err := parser.ParseFile(fset, path.Join(mpkg.Path, file.Name), file.Body, parseOpts) + if err != nil { + panic("error parsing gotypecheck gnobuiltins.go file") + } + files = append(files, f) + pkg, err := g.cfg.Check(mpkg.Path, fset, files, nil) return pkg, err } @@ -196,6 +216,9 @@ func deleteOldIdents(idents map[string]func(), f *ast.File) { } } +/* +// This is how ast filtering would have worked. +// Keep this comment block around in case we need it. func filterCrossing(f *ast.File) (err error) { astutil.Apply(f, nil, func(c *astutil.Cursor) bool { switch n := c.Node().(type) { @@ -226,3 +249,4 @@ func filterCrossing(f *ast.File) (err error) { }) return err } +*/ diff --git a/gnovm/pkg/gnolang/helpers.go b/gnovm/pkg/gnolang/helpers.go index 03be8d25e97..95beacc44cd 100644 --- a/gnovm/pkg/gnolang/helpers.go +++ b/gnovm/pkg/gnolang/helpers.go @@ -11,11 +11,11 @@ import ( // ---------------------------------------- // Functions centralizing definitions -// ReRealmPath and RePackagePath are the regexes used to identify pkgpaths which are meant to +// ReRealmPath and RePPkgPath are the regexes used to identify pkgpaths which are meant to // be realms with persisted states and pure packages. var ( - ReRealmPath = regexp.MustCompile(`^([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.[a-zA-Z]{2,}/r/[a-z0-9_/]+$`) - RePackagePath = regexp.MustCompile(`^([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.[a-zA-Z]{2,}/p/[a-z0-9_/]+$`) + ReRealmPath = regexp.MustCompile(`^([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.[a-zA-Z]{2,}/r/[a-z0-9_/]+$`) + RePPkgPath = regexp.MustCompile(`^([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.[a-zA-Z]{2,}/p/[a-z0-9_/]+$`) ) // ReGnoRunPath is the path used for realms executed in maketx run. @@ -61,9 +61,15 @@ func IsInternalPath(pkgPath string) (base string, isInternal bool) { // IsPurePackagePath determines whether the given pkgpath is for a published Gno package. // It only considers "pure" those starting with gno.land/p/, so it returns false for -// stdlib packages and MsgRun paths. +// stdlib packages, realm paths, and run paths. It also excludes _test paths. func IsPurePackagePath(pkgPath string) bool { - return RePackagePath.MatchString(pkgPath) + if !RePPkgPath.MatchString(pkgPath) { + return false + } + if strings.HasSuffix(pkgPath, "_test") { + return false + } + return true } // IsStdlib determines whether s is a pkgpath for a standard library. diff --git a/gnovm/pkg/gnolang/machine.go b/gnovm/pkg/gnolang/machine.go index e539a8205e2..a10be1f144f 100644 --- a/gnovm/pkg/gnolang/machine.go +++ b/gnovm/pkg/gnolang/machine.go @@ -23,31 +23,31 @@ import ( type Machine struct { // State - Ops []Op // main operations - NumOps int - Values []TypedValue // buffer of values to be operated on - NumValues int // number of values - Exprs []Expr // pending expressions - Stmts []Stmt // pending statements - Blocks []*Block // block (scope) stack - Frames []Frame // func call stack - Package *PackageValue // active package - Realm *Realm // active realm - Alloc *Allocator // memory allocations - Exception *Exception // last exception - NumResults int // number of results returned - Cycles int64 // number of "cpu" cycles - GCCycle int64 // number of "gc" cycles - Stage Stage // add for package init, run otherwise + Ops []Op // main operations + NumOps int + Values []TypedValue // buffer of values to be operated on + NumValues int // number of values + Exprs []Expr // pending expressions + Stmts []Stmt // pending statements + Blocks []*Block // block (scope) stack + Frames []Frame // func call stack + Package *PackageValue // active package + Realm *Realm // active realm + Alloc *Allocator // memory allocations + Exception *Exception // last exception + NumResults int // number of results returned + Cycles int64 // number of "cpu" cycles + GCCycle int64 // number of "gc" cycles + Stage Stage // pre for static eval, add for package init, run otherwise + ReviveEnabled bool // true if revive() enabled (only in testing mode for now) Debugger Debugger // Configuration - PreprocessorMode bool // this is used as a flag when const values are evaluated during preprocessing - Output io.Writer - Store Store - Context any - GasMeter store.GasMeter + Output io.Writer + Store Store + Context any + GasMeter store.GasMeter } // NewMachine initializes a new gno virtual machine, acting as a shorthand @@ -70,16 +70,16 @@ func NewMachine(pkgPath string, store Store) *Machine { // MachineOptions is used to pass options to [NewMachineWithOptions]. type MachineOptions struct { // Active package of the given machine; must be set before execution. - PkgPath string - PreprocessorMode bool - Debug bool - Input io.Reader // used for default debugger input only - Output io.Writer // default os.Stdout - Store Store // default NewStore(Alloc, nil, nil) - Context any - Alloc *Allocator // or see MaxAllocBytes. - MaxAllocBytes int64 // or 0 for no limit. - GasMeter store.GasMeter + PkgPath string + Debug bool + Input io.Reader // used for default debugger input only + Output io.Writer // default os.Stdout + Store Store // default NewStore(Alloc, nil, nil) + Context any + Alloc *Allocator // or see MaxAllocBytes. + MaxAllocBytes int64 // or 0 for no limit. + GasMeter store.GasMeter + ReviveEnabled bool } // the machine constructor gets spammed @@ -101,7 +101,6 @@ var machinePool = sync.Pool{ // Machines initialized through this constructor must be finalized with // [Machine.Release]. func NewMachineWithOptions(opts MachineOptions) *Machine { - preprocessorMode := opts.PreprocessorMode vmGasMeter := opts.GasMeter output := opts.Output @@ -135,7 +134,6 @@ func NewMachineWithOptions(opts MachineOptions) *Machine { if mm.Alloc != nil { mm.Alloc.SetGCFn(func() (int64, bool) { return mm.GarbageCollect() }) } - mm.PreprocessorMode = preprocessorMode mm.Output = output mm.Store = store mm.Context = context @@ -143,6 +141,7 @@ func NewMachineWithOptions(opts MachineOptions) *Machine { mm.Debugger.enabled = opts.Debug mm.Debugger.in = opts.Input mm.Debugger.out = output + mm.ReviveEnabled = opts.ReviveEnabled if pv != nil { mm.SetActivePackage(pv) @@ -399,6 +398,17 @@ func (m *Machine) Stacktrace() (stacktrace Stacktrace) { if m.LastFrame().Func != nil && m.LastFrame().Func.IsNative() { stacktrace.LastLine = -1 // special line for native. } else { + if len(m.Stmts) > 0 { + ls := m.PeekStmt(1) + if bs, ok := ls.(*bodyStmt); ok { + stacktrace.LastLine = bs.LastStmt().GetLine() + } else { + goto NOTPANIC // not a panic call + } + } else { + goto NOTPANIC // not a panic call + } + NOTPANIC: if len(m.Exprs) > 0 { stacktrace.LastLine = m.PeekExpr(1).GetLine() } else if len(m.Stmts) > 0 { @@ -409,6 +419,8 @@ func (m *Machine) Stacktrace() (stacktrace Stacktrace) { } } stacktrace.LastLine = stmt.GetLine() + } else { + stacktrace.LastLine = 0 // dunno } } @@ -498,7 +510,7 @@ func (m *Machine) runFileDecls(withOverrides bool, fns ...*FileNode) []TypedValu // if there is one. for _, fn := range fns { if fn.PkgName != "" && fn.PkgName != m.Package.PkgName { - panic(fmt.Sprintf("expected package name [%s] but got [%s]", + panic(fmt.Sprintf("expected package name [%s] but got [%s]!", m.Package.PkgName, fn.PkgName)) } } @@ -646,7 +658,7 @@ func (m *Machine) runInitFromUpdates(pv *PackageValue, updates []TypedValue) { if strings.HasPrefix(string(fv.Name), "init.") { fb := pv.GetFileBlock(m.Store, fv.FileName) m.PushBlock(fb) - m.runFunc(StageAdd, fv.Name, false) + m.runFunc(StageAdd, fv.Name) m.PopBlock() } } @@ -704,16 +716,12 @@ func (m *Machine) resavePackageValues(rlm *Realm) { // even after running the init function. } -func (m *Machine) runFunc(st Stage, fn Name, withCross bool) { - if withCross { - m.RunStatement(st, S(Call(Call(Nx("cross"), Nx(fn))))) - } else { - m.RunStatement(st, S(Call(Nx(fn)))) - } +func (m *Machine) runFunc(st Stage, fn Name) { + m.RunStatement(st, S(Call(Nx(fn)))) } func (m *Machine) RunMain() { - m.runFunc(StageRun, "main", false) + m.runFunc(StageRun, "main") } // Evaluate throwaway expression in new block scope. @@ -785,7 +793,7 @@ func (m *Machine) EvalStatic(last BlockNode, x Expr) TypedValue { m.PushOp(OpPopBlock) m.PushExpr(x) m.PushOp(OpEval) - m.Run(StageRun) + m.Run(StagePre) res := m.ReapValues(start) if len(res) != 1 { panic("should not happen") @@ -814,7 +822,7 @@ func (m *Machine) EvalStaticTypeOf(last BlockNode, x Expr) Type { m.PushOp(OpPopBlock) m.PushExpr(x) m.PushOp(OpStaticTypeOf) - m.Run(StageRun) + m.Run(StagePre) res := m.ReapValues(start) if len(res) != 1 { panic("should not happen") @@ -1147,9 +1155,7 @@ const ( // main run loop. func (m *Machine) Run(st Stage) { - if m.Context != nil { - m.Stage = st - } + m.Stage = st if bm.OpsEnabled { defer func() { // output each machine run results to file @@ -1162,7 +1168,10 @@ func (m *Machine) Run(st Stage) { if r != nil { switch r := r.(type) { case *Exception: - m.Panic(r.Value) + if r.Stacktrace.IsZero() { + r.Stacktrace = m.Stacktrace() + } + m.pushPanic(r.Value) m.Run(st) default: panic(r) @@ -1224,8 +1233,7 @@ func (m *Machine) Run(st Stage) { m.incrCPU(OpCPUDefer) m.doOpDefer() case OpPanic1: - m.incrCPU(OpCPUPanic1) - m.doOpPanic1() + panic("deprecated") case OpPanic2: m.incrCPU(OpCPUPanic2) m.doOpPanic2() @@ -1792,7 +1800,7 @@ func (m *Machine) PushFrameCall(cx *CallExpr, fv *FuncValue, recv TypedValue, is LastPackage: m.Package, LastRealm: m.Realm, WithCross: withCross, - DidCross: false, + DidCrossing: false, Defers: nil, IsDefer: isDefer, LastException: m.Exception, @@ -1846,11 +1854,19 @@ func (m *Machine) PushFrameCall(cx *CallExpr, fv *FuncValue, recv TypedValue, is if fv.IsCrossing() { if m.Realm != pv.Realm { // panic; not explicit + mrpath := "" + if m.Realm != nil { + mrpath = m.Realm.Path + } + prpath := "" + if pv.Realm != nil { + prpath = pv.Realm.Path + } panic(fmt.Sprintf( "missing cross before external crossing() in %v from %s to %s", fv.String(), - m.Realm.Path, - pv.Realm.Path, + mrpath, + prpath, )) } else { // ok @@ -1880,15 +1896,15 @@ func (m *Machine) PushFrameCall(cx *CallExpr, fv *FuncValue, recv TypedValue, is objpv := m.Store.GetObject(recvPkgOID).(*PackageValue) rlm = objpv.GetRealm() m.Realm = rlm - // DO NOT set DidCross here. Make DidCross only - // happen upon explicit cross(fn)(...) calls - // and subsequent calls to crossing functions - // from the same realm, to avoid user - // confusion. Otherwise whether DidCross - // happened or not depends on where the - // receiver resides, which isn't explicit + // DO NOT set DidCrossing here. Make + // DidCrossing only happen upon explicit + // cross(fn)(...) calls and subsequent calls to + // crossing functions from the same realm, to + // avoid user confusion. Otherwise whether + // DidCrossing happened or not depends on where + // the receiver resides, which isn't explicit // enough to avoid confusion. - // fr.DidCross = true + // fr.DidCrossing = true return } } @@ -2042,6 +2058,18 @@ func (m *Machine) PopUntilLastCallFrame() *Frame { return nil } +// pops until revive (call) frame. +func (m *Machine) PopUntilLastReviveFrame() *Frame { + for i := len(m.Frames) - 1; i >= 0; i-- { + fr := &m.Frames[i] + if fr.IsRevive { + m.Frames = m.Frames[:i+1] + return fr + } + } + return nil +} + func (m *Machine) PushForPointer(lx Expr) { switch lx := lx.(type) { case *NameExpr: @@ -2076,7 +2104,7 @@ func (m *Machine) PushForPointer(lx Expr) { func (m *Machine) PopAsPointer(lx Expr) PointerValue { pv, ro := m.PopAsPointer2(lx) if ro { - panic("cannot modify external-realm or non-realm object") + m.Panic(typedString("cannot directly modify readonly tainted object (w/o method): " + lx.String())) } return pv } @@ -2186,11 +2214,27 @@ func (m *Machine) CheckEmpty() error { } } +// This function does go-panic. +// To stop execution immediately stdlib native code MUST use this rather than +// pushPanic(). +// Some code in realm.go and values.go will panic(&Exception{...}) directly. +// Keep this code in sync with those calls. +// Note that m.Run() will fill in the stacktrace if it isn't present. +func (m *Machine) Panic(etv TypedValue) { + // Construct a new exception. + ex := &Exception{ + Value: etv, + Stacktrace: m.Stacktrace(), + } + // Panic immediately. + panic(ex) +} + // This function does not go-panic: // caller must return manually. -// NOTE: duplicated in "panic" uverse function. -// XXX deduplicate -func (m *Machine) Panic(etv TypedValue) { +// It should ONLY be called from doOp* Op handlers, +// and should return immediately from the origin Op. +func (m *Machine) pushPanic(etv TypedValue) { // Construct a new exception. ex := &Exception{ Value: etv, @@ -2300,7 +2344,7 @@ func (m *Machine) String() string { builder := &sb // Pointer for use in fmt.Fprintf. builder.Grow(totalLength) - fmt.Fprintf(builder, "Machine:\n PreprocessorMode: %v\n Op: %v\n Values: (len: %d)\n", m.PreprocessorMode, m.Ops[:m.NumOps], m.NumValues) + fmt.Fprintf(builder, "Machine:\n Stage: %v\n Op: %v\n Values: (len: %d)\n", m.Stage, m.Ops[:m.NumOps], m.NumValues) for i := m.NumValues - 1; i >= 0; i-- { fmt.Fprintf(builder, " #%d %v\n", i, m.Values[i]) diff --git a/gnovm/pkg/gnolang/machine_test.go b/gnovm/pkg/gnolang/machine_test.go index f945b6d9742..583b6106b37 100644 --- a/gnovm/pkg/gnolang/machine_test.go +++ b/gnovm/pkg/gnolang/machine_test.go @@ -71,7 +71,7 @@ func TestMachineString(t *testing.T) { { "created with defaults", NewMachineWithOptions(MachineOptions{}), - "Machine:\n PreprocessorMode: false\n Op: []\n Values: (len: 0)\n Exprs:\n Stmts:\n Blocks:\n Blocks (other):\n Frames:\n", + "Machine:\n Stage: \n Op: []\n Values: (len: 0)\n Exprs:\n Stmts:\n Blocks:\n Blocks (other):\n Frames:\n", }, { "created with store and defaults", @@ -82,7 +82,7 @@ func TestMachineString(t *testing.T) { store := NewStore(nil, baseStore, iavlStore) return NewMachine("std", store) }(), - "Machine:\n PreprocessorMode: false\n Op: []\n Values: (len: 0)\n Exprs:\n Stmts:\n Blocks:\n Blocks (other):\n Frames:\n", + "Machine:\n Stage: \n Op: []\n Values: (len: 0)\n Exprs:\n Stmts:\n Blocks:\n Blocks (other):\n Frames:\n", }, { "filled in", @@ -101,7 +101,7 @@ func TestMachineString(t *testing.T) { m.PushStmts(S(Call(X("Redecl"), 11))) return m }(), - "Machine:\n PreprocessorMode: false\n Op: [OpHalt]\n Values: (len: 0)\n Exprs:\n #0 100\n Stmts:\n #0 Redecl(11)\n Blocks:\n Blocks (other):\n Frames:\n", + "Machine:\n Stage: \n Op: [OpHalt]\n Values: (len: 0)\n Exprs:\n #0 100\n Stmts:\n #0 Redecl(11)\n Blocks:\n Blocks (other):\n Frames:\n", }, } diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index 6463a264dc5..5dd1aae6b43 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -971,6 +971,10 @@ func (x *bodyStmt) PopActiveStmt() (as Stmt) { return } +func (x *bodyStmt) LastStmt() Stmt { + return x.Body[x.NextBodyIndex-1] +} + func (x *bodyStmt) String() string { next := "" if x.NextBodyIndex < 0 { @@ -1440,7 +1444,7 @@ func (x *PackageNode) NewPackage() *PackageValue { FBlocks: nil, fBlocksMap: make(map[Name]*Block), } - if IsRealmPath(x.PkgPath) { + if IsRealmPath(x.PkgPath) || x.PkgPath == "main" { rlm := NewRealm(x.PkgPath) pv.SetRealm(rlm) } diff --git a/gnovm/pkg/gnolang/op_assign.go b/gnovm/pkg/gnolang/op_assign.go index b914662e932..b4291515f82 100644 --- a/gnovm/pkg/gnolang/op_assign.go +++ b/gnovm/pkg/gnolang/op_assign.go @@ -12,7 +12,7 @@ func (m *Machine) doOpDefine() { nx := s.Lhs[i].(*NameExpr) // Finally, define (or assign if loop block). ptr := lb.GetPointerToMaybeHeapDefine(m.Store, nx) - if !m.PreprocessorMode && isUntyped(rvs[i].T) && rvs[i].T.Kind() != BoolKind { + if m.Stage != StagePre && isUntyped(rvs[i].T) && rvs[i].T.Kind() != BoolKind { panic("untyped conversion should not happen at runtime") } ptr.Assign2(m.Alloc, m.Store, m.Realm, rvs[i], true) @@ -28,7 +28,7 @@ func (m *Machine) doOpAssign() { for i := len(s.Lhs) - 1; 0 <= i; i-- { // Pop lhs value and desired type. lv := m.PopAsPointer(s.Lhs[i]) - if !m.PreprocessorMode && isUntyped(rvs[i].T) && rvs[i].T.Kind() != BoolKind { + if m.Stage != StagePre && isUntyped(rvs[i].T) && rvs[i].T.Kind() != BoolKind { panic("untyped conversion should not happen at runtime") } lv.Assign2(m.Alloc, m.Store, m.Realm, rvs[i], true) diff --git a/gnovm/pkg/gnolang/op_binary.go b/gnovm/pkg/gnolang/op_binary.go index fad6e5b4fb0..6f930eabc7e 100644 --- a/gnovm/pkg/gnolang/op_binary.go +++ b/gnovm/pkg/gnolang/op_binary.go @@ -1172,7 +1172,7 @@ func shlAssign(m *Machine, lv, rv *TypedValue) { rv.AssertNonNegative("runtime error: negative shift amount") checkOverflow := func(v func() bool) { - if m.PreprocessorMode && !v() { + if m.Stage == StagePre && !v() { panic(`constant overflows`) } } @@ -1296,7 +1296,7 @@ func shrAssign(m *Machine, lv, rv *TypedValue) { rv.AssertNonNegative("runtime error: negative shift amount") checkOverflow := func(v func() bool) { - if m.PreprocessorMode && !v() { + if m.Stage == StagePre && !v() { panic(`constant overflows`) } } diff --git a/gnovm/pkg/gnolang/op_call.go b/gnovm/pkg/gnolang/op_call.go index 33923137976..574ab028f20 100644 --- a/gnovm/pkg/gnolang/op_call.go +++ b/gnovm/pkg/gnolang/op_call.go @@ -144,22 +144,45 @@ func (m *Machine) doOpCallDeferNativeBody() { fv.nativeBody(m) } -// Assumes that result values are pushed onto the Values stack. -func (m *Machine) doOpReturn() { - // Unwind stack. - cfr := m.PopUntilLastCallFrame() - - // See if we are exiting a realm boundary. +// Used by return and panic operation handlers. +// Must finalize for returns, and must abort for panics. +func (m *Machine) isRealmBoundary(cfr *Frame) bool { crlm := m.Realm if crlm != nil { - if cfr.DidCross { - // Finalize realm updates! - // NOTE: This is a resource intensive undertaking. - crlm.FinalizeRealmTransaction(m.Store) + prlm := cfr.LastRealm + if cfr.WithCross { + // Even if crlm == prlm, must finalize + // to preserve attachment rules. + return true + } else if crlm != prlm { + // .WithCross was already handled; + // This is for implicitly crossed + // borrow-realms, the storage realm + // of a method's receiver. + return true + } else if m.NumFrames() == 1 { + // We are exiting the machine's realm. + return true } } + return false +} + +// Finalize realm updates if realm boundary. +// NOTE: resource intensive +func (m *Machine) maybeFinalize(cfr *Frame) { + if m.isRealmBoundary(cfr) { + m.Realm.FinalizeRealmTransaction(m.Store) + } +} - // Finalize +// Assumes that result values are pushed onto the Values stack. +func (m *Machine) doOpReturn() { + // Unwind stack. + cfr := m.PopUntilLastCallFrame() + // Finalize if exiting realm boundary. + m.maybeFinalize(cfr) + // Reset to before frame. m.PopFrameAndReturn() } @@ -181,27 +204,9 @@ func (m *Machine) doOpReturnAfterCopy() { // Unwind stack. cfr = m.PopUntilLastCallFrame() - - // See if we are exiting a realm boundary. - crlm := m.Realm - if crlm != nil { - lrlm := cfr.LastRealm - finalize := false - if m.NumFrames() == 1 { - // We are exiting the machine's realm. - finalize = true - } else if crlm != lrlm { - // We are changing realms or exiting a realm. - finalize = true - } - if finalize { - // Finalize realm updates! - // NOTE: This is a resource intensive undertaking. - crlm.FinalizeRealmTransaction(m.Store) - } - } - - // Finalize + // Finalize if exiting realm boundary. + m.maybeFinalize(cfr) + // Reset to before frame. m.PopFrameAndReturn() } @@ -209,6 +214,7 @@ func (m *Machine) doOpReturnAfterCopy() { // i.e. named result vars declared in func signatures, // because return was called with no return arguments. func (m *Machine) doOpReturnFromBlock() { + // Copy results from block. cfr := m.PopUntilLastCallFrame() fv := cfr.Func @@ -220,25 +226,10 @@ func (m *Machine) doOpReturnFromBlock() { rtv := *fillValueTV(m.Store, &fblock.Values[i+numParams]) m.PushValueFromBlock(rtv) } - // See if we are exiting a realm boundary. - crlm := m.Realm - if crlm != nil { - lrlm := cfr.LastRealm - finalize := false - if m.NumFrames() == 1 { - // We are exiting the machine's realm. - finalize = true - } else if crlm != lrlm { - // We are changing realms or exiting a realm. - finalize = true - } - if finalize { - // Finalize realm updates! - // NOTE: This is a resource intensive undertaking. - crlm.FinalizeRealmTransaction(m.Store) - } - } - // finalize + + // Finalize if exiting realm boundary. + m.maybeFinalize(cfr) + // Reset to before frame. m.PopFrameAndReturn() } @@ -268,6 +259,23 @@ func (m *Machine) doOpReturnCallDefers() { // If still in panic state pop this frame so doOpPanic2() will // try doOpReturnCallDefers() in the previous frame. if m.Exception != nil { + // If crossing a realm boundary find the revive frame + // for transaction revival. + if m.isRealmBoundary(cfr) { + cfr := m.PopUntilLastReviveFrame() + if cfr == nil { + // or abort the transaction. + panic(m.makeUnhandledPanicError()) + } + m.PopFrameAndReturn() + // assign exception as return of revive(). + resx := m.PeekValue(1) + resx.Assign(m.Alloc, m.Exception.Value, false) + m.Exception = nil // reset + return + } + // Handle panic by calling OpReturnCallDefers on + // the next (last) call frame) m.PopFrame() m.PushOp(OpPanic2) } else { @@ -279,7 +287,7 @@ func (m *Machine) doOpReturnCallDefers() { } if dfr.Func == nil { - m.Panic(typedString("defer called a nil function")) + m.pushPanic(typedString("defer called a nil function")) return } @@ -426,41 +434,36 @@ func (m *Machine) doOpDefer() { Func: nil, }) default: - m.Panic(typedString(fmt.Sprintf("invalid defer function call: %v", cv))) + m.pushPanic(typedString(fmt.Sprintf("invalid defer function call: %v", cv))) return } m.PopValue() // pop func } -// XXX DEPRECATED -// -//nolint:govet // unreachable code -func (m *Machine) doOpPanic1() { - panic("doOpPanic1 is deprecated") - // Pop exception - var ex TypedValue = m.PopValue().Copy(m.Alloc) - // Panic - m.Panic(ex) - return +// Build exception string just as go, separated by \n\t. +// TODO: deprecate UnhandledPanicError and just use the Exception. +// (use a field to mark transaction abort) +func (m *Machine) makeUnhandledPanicError() UnhandledPanicError { + numExceptions := m.Exception.NumExceptions() + exs := make([]string, numExceptions) + last := m.Exception + for i := 0; i < numExceptions; i++ { + exs[numExceptions-1-i] = last.Sprint(m) + last = last.Previous + } + return UnhandledPanicError{ + Descriptor: strings.Join(exs, "\n\t"), + } } func (m *Machine) doOpPanic2() { if m.Exception == nil { panic("should not happen") } - last := m.PopUntilLastCallFrame() - if last == nil { - // Build exception string just as go, separated by \n\t. - numExceptions := m.Exception.NumExceptions() - exs := make([]string, numExceptions) - last := m.Exception - for i := 0; i < numExceptions; i++ { - exs[numExceptions-1-i] = last.Sprint(m) - last = last.Previous - } - panic(UnhandledPanicError{ - Descriptor: strings.Join(exs, "\n\t"), - }) + cfr := m.PopUntilLastCallFrame() + if cfr == nil { + // panic(m.makeUnhandledPanicError()) + panic("should not happen") } m.PushOp(OpReturnCallDefers) } diff --git a/gnovm/pkg/gnolang/op_decl.go b/gnovm/pkg/gnolang/op_decl.go index 1734b9fdec9..0565c0b065e 100644 --- a/gnovm/pkg/gnolang/op_decl.go +++ b/gnovm/pkg/gnolang/op_decl.go @@ -34,7 +34,7 @@ func (m *Machine) doOpValueDecl() { if isUntyped(tv.T) { if !s.Const { - if !m.PreprocessorMode && rvs[i].T.Kind() != BoolKind { + if m.Stage != StagePre && rvs[i].T.Kind() != BoolKind { panic("untyped conversion should not happen at runtime") } ConvertUntypedTo(&tv, nil) diff --git a/gnovm/pkg/gnolang/op_expressions.go b/gnovm/pkg/gnolang/op_expressions.go index 3449ac8f16d..dc7346205d2 100644 --- a/gnovm/pkg/gnolang/op_expressions.go +++ b/gnovm/pkg/gnolang/op_expressions.go @@ -137,7 +137,7 @@ func (m *Machine) doOpStar() { switch bt := baseOf(xv.T).(type) { case *PointerType: if xv.V == nil { - m.Panic(typedString("nil pointer dereference")) + m.pushPanic(typedString("nil pointer dereference")) return } @@ -197,7 +197,7 @@ func (m *Machine) doOpTypeAssert1() { if xt == nil || xv.IsNilInterface() { // TODO: default panic type? ex := fmt.Sprintf("interface conversion: interface is nil, not %s", t.String()) - m.Panic(typedString(ex)) + m.pushPanic(typedString(ex)) return } @@ -210,7 +210,7 @@ func (m *Machine) doOpTypeAssert1() { "non-concrete %s doesn't implement %s", xt.String(), it.String()) - m.Panic(typedString(ex)) + m.pushPanic(typedString(ex)) return } @@ -224,7 +224,7 @@ func (m *Machine) doOpTypeAssert1() { xt.String(), it.String(), err.Error()) - m.Panic(typedString(ex)) + m.pushPanic(typedString(ex)) return } // NOTE: consider ability to push an @@ -236,7 +236,7 @@ func (m *Machine) doOpTypeAssert1() { } else { // is concrete assert if xt == nil { ex := fmt.Sprintf("nil is not of type %s", t.String()) - m.Panic(typedString(ex)) + m.pushPanic(typedString(ex)) return } @@ -250,7 +250,7 @@ func (m *Machine) doOpTypeAssert1() { "%s is not of type %s", xt.String(), t.String()) - m.Panic(typedString(ex)) + m.pushPanic(typedString(ex)) return } // keep cxt as is. diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index c1f5fdd7776..5e3462b31ea 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -1408,7 +1408,13 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { } pc.SetWithCross() } else if fv.PkgPath == uversePkgPath && fv.Name == "crossing" { - // XXX Make sure it's only used in a realm. + pn := packageOf(last) + if !IsRealmPath(pn.PkgPath) { + panic("crossing() is only allowed in realm packages") + } + } else if fv.PkgPath == uversePkgPath && fv.Name == "attach" { + // reserve attach() so we can support it later. + panic("attach() not yet supported") } } @@ -3353,7 +3359,6 @@ func evalConst(store Store, last BlockNode, x Expr) *ConstExpr { // is constant? From the machine? m := NewMachine(".dontcare", store) cv := m.EvalStatic(last, x) - m.PreprocessorMode = false m.Release() cx = &ConstExpr{ Source: x, @@ -4763,8 +4768,7 @@ func tryPredefine(store Store, pkg *PackageNode, last BlockNode, d Decl) (un Nam } // NOTE: imports from "pure packages" are actually sometimes - // allowed, most notably in MsgRun and filetests; IsPurePackagePath - // returns false in these cases. + // allowed, most notably filetests. if IsPurePackagePath(pkg.PkgPath) && IsRealmPath(d.PkgPath) { panic(fmt.Sprintf("pure package path %q cannot import realm path %q", pkg.PkgPath, d.PkgPath)) } diff --git a/gnovm/pkg/gnolang/uverse.go b/gnovm/pkg/gnolang/uverse.go index b63ae9fb1d3..7d15e6d993b 100644 --- a/gnovm/pkg/gnolang/uverse.go +++ b/gnovm/pkg/gnolang/uverse.go @@ -601,7 +601,7 @@ func makeUverseNode() { ci := int(cv.ConvertGetInt()) if ci < li { - panic(&Exception{Value: typedString(`makeslice: cap out of range`)}) + m.Panic(typedString(`makeslice: cap out of range`)) } if et.Kind() == Uint8Kind { @@ -732,7 +732,10 @@ func makeUverseNode() { func(m *Machine) { arg0 := m.LastBlock().GetParams1(m.Store) ex := arg0.TV.Copy(m.Alloc) - m.Panic(ex) + // m.Panic(ex) also works, but after return will immediately OpPanic2. + // This should be the only place .pushPanic() is called + // outside of op_*.go doOp*() functions. + m.pushPanic(ex) }, ) defNative("recover", @@ -765,9 +768,9 @@ func makeUverseNode() { if !fr1.LastPackage.IsRealm() { panic("crossing call only allowed in realm packages") // XXX test } - // Verify prior fr.WithCross or fr.DidCross. + // Verify prior fr.WithCross or fr.DidCrossing. // NOTE: fr.WithCross may or may not be true, - // crossing() (which sets fr.DidCross) can be + // crossing() (which sets fr.DidCrossing) can be // stacked. for i := 1 + 1; ; i++ { fri := m.PeekCallFrame(i) @@ -784,19 +787,19 @@ func makeUverseNode() { // meains fri.WithCross would have been // found below. fr2 := m.PeekCallFrame(2) - fr2.SetDidCross() + fr2.SetDidCrossing() return } - if fri.WithCross || fri.DidCross { - // NOTE: fri.DidCross implies + if fri.WithCross || fri.DidCrossing { + // NOTE: fri.DidCrossing implies // everything under it is also valid. - // fri.DidCross && !fri.WithCross + // fri.DidCrossing && !fri.WithCross // can happen with an implicit switch. fr2 := m.PeekCallFrame(2) - fr2.SetDidCross() + fr2.SetDidCrossing() return } - // Neither fri.WithCross nor fri.DidCross, yet + // Neither fri.WithCross nor fri.DidCrossing, yet // Realm already switched implicitly. if fri.LastRealm != m.Realm { panic("crossing could not find corresponding cross(fn)(...) call") @@ -822,6 +825,77 @@ func makeUverseNode() { */ }, ) + defNative("attach", + Flds( // params + "xs", Vrd(AnyT()), // args[0] + ), + nil, // results + func(m *Machine) { + panic("attach() is not yet supported") + }, + ) + // Typed nils in Go1 are problematic. + // https://dave.cheney.net/2017/08/09/typed-nils-in-go-2 + // Dave Cheney suggests typed-nil == nil when the typed-nil is not an + // interface type, but arguably it should be the other way around, e.g. + // > (*int)(nil) != nil. + // Since Gno doesn't yet support reflect, and since even with reflect + // implementing istypednil() is annoying, while istypednil() shouldn't + // require reflect, Gno should therefore offer istypednil() as a uverse + // function. + defNative("istypednil", + Flds( // params + "x", AnyT(), + ), + Flds( // results + "", "bool", + ), + func(m *Machine) { + arg0 := m.LastBlock().GetParams1(m.Store) + m.PushValue(typedBool(arg0.TV.IsTypedNil())) + }, + ) + // In the final form, it will do nothing if no abort; but otherwise + // will make it as if nothing happened (with full cache wrapping). This + // gives programs precognition, or at least hypotheticals. + // e.g. "If it **would have** done this, do that instead". + // + // XXX This is only enabled in testing mode (for now), and test + // developers should be aware that behavior will change to be like + // above; currently it doesn't cache-wrap the fn function so residual + // state mutations remain even after revive(), but they will be + // "magically" rolled back upon panic in the future. The fn function + // must *always* panic in the end in order to prevent state mutations + // after a non-aborting transaction. + defNative("revive", + Flds( // params + "fn", FuncT(nil, nil), + ), + Flds( // results + "ex", AnyT(), + ), + func(m *Machine) { + arg0 := m.LastBlock().GetParams1(m.Store) + if m.ReviveEnabled { + last := m.LastFrame() + + // Push the no-abort result. + // last.SetRevive() marks the frame and this + // value will get replaced w/ exception. + m.PushValue(TypedValue{}) + last.SetIsRevive() + + // Push function and precall it. + m.PushExpr(Call(&ConstExpr{Source: X("fn"), TypedValue: *arg0.TV})) + m.PushOp(OpPrecall) + m.PushValue(*arg0.TV) + } else { + // If revive isn't enabled just panic. + m.pushPanic(typedString("revive() not enabled")) + // m.PushValue(TypedValue{}) + } + }, + ) uverseValue = uverseNode.NewPackage() } diff --git a/gnovm/pkg/gnolang/uverse_test.go b/gnovm/pkg/gnolang/uverse_test.go index 4db4b233f42..3208508db99 100644 --- a/gnovm/pkg/gnolang/uverse_test.go +++ b/gnovm/pkg/gnolang/uverse_test.go @@ -200,7 +200,7 @@ func BenchmarkGnoPrintln(b *testing.B) { for i := 0; i < b.N; i++ { buf.Reset() - m.RunStatement(StageRun, S(Call(Nx("main")))) + m.RunMain() pSink = buf.String() } @@ -292,7 +292,7 @@ func TestGnoPrintAndPrintln(t *testing.T) { }, false) buf.Reset() - m.RunStatement(StageRun, S(Call(Nx("main")))) + m.RunMain() got := buf.String() assert.Equal(t, tt.want, got) }) diff --git a/gnovm/pkg/gnolang/values.go b/gnovm/pkg/gnolang/values.go index 35a89412207..485ece70760 100644 --- a/gnovm/pkg/gnolang/values.go +++ b/gnovm/pkg/gnolang/values.go @@ -961,6 +961,16 @@ func (tv *TypedValue) IsUndefined() bool { return tv.T == nil } +func (tv *TypedValue) IsTypedNil() bool { + if tv.V != nil { + return false + } + if tv.T != nil && tv.T.Kind() == PointerKind { + return true + } + return false +} + // (this is used mostly by the preprocessor) func (tv *TypedValue) IsNilInterface() bool { if tv.T != nil && tv.T.Kind() == InterfaceKind { @@ -2562,6 +2572,12 @@ func typedInt(i int) TypedValue { return tv } +func typedBool(b bool) TypedValue { + tv := TypedValue{T: BoolType} + tv.SetBool(b) + return tv +} + func untypedBool(b bool) TypedValue { tv := TypedValue{T: UntypedBoolType} tv.SetBool(b) diff --git a/gnovm/pkg/gnolang/values_string.go b/gnovm/pkg/gnolang/values_string.go index 4a75de5f1c0..ab36c0c1e47 100644 --- a/gnovm/pkg/gnolang/values_string.go +++ b/gnovm/pkg/gnolang/values_string.go @@ -379,9 +379,13 @@ func (tv *TypedValue) ProtectedSprint(seen *seenValues, considerDeclaredType boo } case *PointerType: if tv.V == nil { - return "invalid-pointer" + return "typed-nil" } - return tv.V.(PointerValue).ProtectedString(seen) + roPre, roPost := "", "" + if tv.IsReadonly() { + roPre, roPost = "readonly(", ")" + } + return roPre + tv.V.(PointerValue).ProtectedString(seen) + roPost case *FuncType: switch fv := tv.V.(type) { case nil: @@ -414,13 +418,17 @@ func (tv *TypedValue) ProtectedSprint(seen *seenValues, considerDeclaredType boo if tv.V == nil { return "(" + nilStr + " " + tv.T.String() + ")" } - + // Value may be N_Readonly + roPre, roPost := "", "" + if tv.IsReadonly() { + roPre, roPost = "readonly(", ")" + } // *ArrayType, *SliceType, *StructType, *MapType if ps, ok := tv.V.(protectedStringer); ok { - return ps.ProtectedString(seen) + return roPre + ps.ProtectedString(seen) + roPost } else if s, ok := tv.V.(fmt.Stringer); ok { // *NativeType - return s.String() + return roPre + s.String() + roPost } if debug { diff --git a/gnovm/pkg/gnomod/pkg.go b/gnovm/pkg/gnomod/pkg.go index 85f1d31442d..9691de13572 100644 --- a/gnovm/pkg/gnomod/pkg.go +++ b/gnovm/pkg/gnomod/pkg.go @@ -112,7 +112,7 @@ func ListPkgs(root string) (PkgList, error) { } gnoMod.Sanitize() if err := gnoMod.Validate(); err != nil { - return fmt.Errorf("validate: %w", err) + return fmt.Errorf("failed to validate gno.mod in %s: %w", gnoModPath, err) } pkg, err := gnolang.ReadMemPackage(path, gnoMod.Module.Mod.Path) diff --git a/gnovm/pkg/test/filetest.go b/gnovm/pkg/test/filetest.go index a9502952ab3..831d1174ddf 100644 --- a/gnovm/pkg/test/filetest.go +++ b/gnovm/pkg/test/filetest.go @@ -8,6 +8,7 @@ import ( "io" "regexp" "runtime/debug" + "slices" "strconv" "strings" @@ -61,6 +62,7 @@ func (opts *TestOptions) runFiletest(filename string, source []byte) (string, er Context: ctx, MaxAllocBytes: maxAlloc, Debug: opts.Debug, + ReviveEnabled: true, }) defer m.Release() result := opts.runTest(m, pkgPath, filename, source, opslog) @@ -71,7 +73,7 @@ func (opts *TestOptions) runFiletest(filename string, source []byte) (string, er // multiple mismatches occurred. updated := false var returnErr error - // match verifies the content against dir.Content; if different, + // `match` verifies the content against dir.Content; if different, // either updates dir.Content (for opts.Sync) or appends a new returnErr. match := func(dir *Directive, actual string) { content := dir.Content @@ -82,30 +84,49 @@ func (opts *TestOptions) runFiletest(filename string, source []byte) (string, er dir.Content = actual updated = true } else { - returnErr = multierr.Append( - returnErr, - fmt.Errorf("%s diff:\n%s", dir.Name, unifiedDiff(content, actual)), - ) + if dir.Name == DirectiveError { + returnErr = multierr.Append( + returnErr, + fmt.Errorf("%s diff:\n%s\nstacktrace:\n%s\nstack:\n%v", + dir.Name, unifiedDiff(content, actual), + result.GnoStacktrace, string(result.GoPanicStack)), + ) + } else { + returnErr = multierr.Append( + returnErr, + fmt.Errorf("%s diff:\n%s", dir.Name, unifiedDiff(content, actual)), + ) + } } } } - // Check if we have an error, whether we're supposed to get it. + // Ensure needed the directives are present. if result.Error != "" { // Ensure this error was supposed to happen. errDirective := dirs.First(DirectiveError) if errDirective == nil { - return "", fmt.Errorf("unexpected panic: %s\noutput:\n%s\nstacktrace:%s\nstack:\n%v", - result.Error, result.Output, result.GnoStacktrace, string(result.GoPanicStack)) + if opts.Sync { + dirs = append(dirs, Directive{ + Name: DirectiveError, + Content: "", + }) + } else { + return "", fmt.Errorf("unexpected panic: %s\noutput:\n%s\nstacktrace:\n%s\nstack:\n%v", + result.Error, result.Output, result.GnoStacktrace, string(result.GoPanicStack)) + } } - - // The Error directive (and many others) will have one trailing newline, - // which is not in the output - so add it there. - match(errDirective, result.Error+"\n") } else if result.Output != "" { outputDirective := dirs.First(DirectiveOutput) if outputDirective == nil { - return "", fmt.Errorf("unexpected output:\n%s", result.Output) + if opts.Sync { + dirs = append(dirs, Directive{ + Name: DirectiveOutput, + Content: "", + }) + } else { + return "", fmt.Errorf("unexpected output:\n%s", result.Output) + } } } else { err = m.CheckEmpty() @@ -125,10 +146,9 @@ func (opts *TestOptions) runFiletest(filename string, source []byte) (string, er dir := &dirs[idx] switch dir.Name { case DirectiveOutput: - if !strings.HasSuffix(result.Output, "\n") { - result.Output += "\n" - } match(dir, trimTrailingSpaces(result.Output)) + case DirectiveError: + match(dir, result.Error) case DirectiveRealm: res := opslog.(*bytes.Buffer).String() match(dir, res) @@ -138,11 +158,11 @@ func (opts *TestOptions) runFiletest(filename string, source []byte) (string, er if err != nil { panic(err) } - evtstr := string(evtjson) + "\n" + evtstr := string(evtjson) match(dir, evtstr) case DirectivePreprocessed: pn := m.Store.GetBlockNode(gno.PackageNodeLocation(pkgPath)) - pre := pn.(*gno.PackageNode).FileSet.Files[0].String() + "\n" + pre := pn.(*gno.PackageNode).FileSet.Files[0].String() match(dir, pre) case DirectiveStacktrace: match(dir, result.GnoStacktrace) @@ -280,7 +300,7 @@ func (opts *TestOptions) runTest(m *gno.Machine, pkgPath, filename string, conte n := gno.MustParseFile(filename, string(content)) m.RunFiles(n) - m.RunStatement(gno.StageRun, gno.S(gno.Call(gno.X("main")))) + m.RunMain() } else { // Realm case. gno.DisableDebug() // until main call. @@ -321,9 +341,6 @@ func (opts *TestOptions) runTest(m *gno.Machine, pkgPath, filename string, conte gno.EnableDebug() // clear store.opslog from init function(s). m.Store.SetLogStoreOps(opslog) // resets. - // Call main() like withrealm(main)(). - // This will switch the realm to the package. - // main() must start with crossing(). m.RunMain() } @@ -355,6 +372,19 @@ const ( DirectiveTypeCheckError = "TypeCheckError" ) +var allDirectives = []string{ + DirectivePkgPath, + DirectiveMaxAlloc, + DirectiveSend, + DirectiveOutput, + DirectiveError, + DirectiveRealm, + DirectiveEvents, + DirectivePreprocessed, + DirectiveStacktrace, + DirectiveTypeCheckError, +} + // Directives contains the directives of a file. // It may also contains directives with empty names, to indicate parts of the // original source file (used to re-construct the filetest at the end). @@ -398,6 +428,9 @@ func (d Directives) FileTest() string { case strings.ToUpper(dir.Name) == dir.Name: // ALLCAPS: bld.WriteString("// " + dir.Name + ": " + dir.Content + ll) default: + if dir.Content == "" || dir.Content == "\n" { + continue + } bld.WriteString("// " + dir.Name + ":\n") cnt := strings.TrimRight(dir.Content, "\n ") lines := strings.Split(cnt, "\n") @@ -478,50 +511,60 @@ func ParseDirectives(source io.Reader) (Directives, error) { // Find if there is a colon (indicating a possible directive). subm := reDirectiveLine.FindStringSubmatch(comment) - switch { - case subm == nil: // comment... - // If we're already in an incomplete directive, simply append there. - if !last.Complete { - if last.Name == "" { - last.Content += txt + "\n" - last.LastLine = txt - continue - } else { - last.Content += comment + "\n" - last.LastLine = txt - continue - } - } - // Otherwise make a new directive. - parsed = append(parsed, - Directive{ - Content: txt + "\n", - LastLine: txt, - }) - case subm[1] != "": // CamelCase: - // output directive, with content on newlines + if subm != nil && slices.Contains(allDirectives, subm[1]) { + // CamelCase directive. parsed = append(parsed, Directive{ Name: subm[1], LastLine: txt, }) - default: // subm[2] != "" // ALLCAPS: ... + continue + } + if subm != nil && slices.Contains(allDirectives, subm[2]) { + // APPCAPS directive. parsed = append(parsed, Directive{ Name: subm[2], Content: subm[3], Complete: true, }) + continue } - } - /* - for i, dir := range parsed { - fmt.Printf("#%d %s: [[[%s]]]\n", i, dir.Name, dir.Content) + // Not a directive, just a comment. + // If we're already in an incomplete directive, simply append there. + if !last.Complete { + if last.Name == "" { + last.Content += txt + "\n" + last.LastLine = txt + continue + } else { + last.Content += comment + "\n" + last.LastLine = txt + continue + } } - */ + // Otherwise make a new directive. + parsed = append(parsed, + Directive{ + Content: txt + "\n", + LastLine: txt, + }) + } - parsed = parsed[1:] + // Remove trailing (newline|space)* and filter empty directives. + result := make([]Directive, 0, len(parsed)) + parsed = parsed[1:] // remove faux directive + for _, dir := range parsed { + content := dir.Content + content = strings.TrimRight(content, "\n ") + if content == "" { + continue + } + dir.Content = content + result = append(result, dir) + // fmt.Printf("#%d %s: [[[%s]]]\n", i, dir.Name, dir.Content) + } - return parsed, sc.Err() + return result, sc.Err() } diff --git a/gnovm/pkg/test/imports.go b/gnovm/pkg/test/imports.go index 5dcaac6fa28..c5be852be46 100644 --- a/gnovm/pkg/test/imports.go +++ b/gnovm/pkg/test/imports.go @@ -77,10 +77,11 @@ func StoreWithOptions( send := std.Coins{} ctx := Context("", pkgPath, send) m2 := gno.NewMachineWithOptions(gno.MachineOptions{ - PkgPath: "test", - Output: output, - Store: store, - Context: ctx, + PkgPath: "test", + Output: output, + Store: store, + Context: ctx, + ReviveEnabled: true, }) return processMemPackage(m2, memPkg, true) } @@ -103,10 +104,11 @@ func StoreWithOptions( send := std.Coins{} ctx := Context("", pkgPath, send) m2 := gno.NewMachineWithOptions(gno.MachineOptions{ - PkgPath: "test", - Output: output, - Store: store, - Context: ctx, + PkgPath: "test", + Output: output, + Store: store, + Context: ctx, + ReviveEnabled: true, }) return processMemPackage(m2, memPkg, true) } @@ -155,9 +157,10 @@ func loadStdlib(rootDir, pkgPath string, store gno.Store, stdout io.Writer, prep // NOTE: see also pkgs/sdk/vm/builtins.go // Needs PkgPath != its name because TestStore.getPackage is the package // getter for the store, which calls loadStdlib, so it would be recursively called. - PkgPath: "stdlibload", - Output: stdout, - Store: store, + PkgPath: "stdlibload", + Output: stdout, + Store: store, + ReviveEnabled: true, }) if preprocessOnly { m2.Store.AddMemPackage(memPkg) diff --git a/gnovm/pkg/test/test.go b/gnovm/pkg/test/test.go index 150daae1a3b..322b1f3fe0a 100644 --- a/gnovm/pkg/test/test.go +++ b/gnovm/pkg/test/test.go @@ -73,10 +73,11 @@ func Context(caller crypto.Bech32Address, pkgPath string, send std.Coins) *tests // It is only used for linting/preprocessing. func Machine(testStore gno.Store, output io.Writer, pkgPath string, debug bool) *gno.Machine { return gno.NewMachineWithOptions(gno.MachineOptions{ - Store: testStore, - Output: output, - Context: Context("", pkgPath, nil), - Debug: debug, + Store: testStore, + Output: output, + Context: Context("", pkgPath, nil), + Debug: debug, + ReviveEnabled: true, }) } diff --git a/gnovm/stdlibs/std/crypto.go b/gnovm/stdlibs/std/crypto.go index e019ea31202..8e12eb4d0f3 100644 --- a/gnovm/stdlibs/std/crypto.go +++ b/gnovm/stdlibs/std/crypto.go @@ -5,5 +5,6 @@ import ( ) func X_derivePkgAddr(pkgPath string) string { - return string(gno.DerivePkgBech32Addr(pkgPath)) + addr := string(gno.DerivePkgBech32Addr(pkgPath)) + return addr } diff --git a/gnovm/stdlibs/std/emit_event_test.go b/gnovm/stdlibs/std/emit_event_test.go index 55362aff031..05532d49707 100644 --- a/gnovm/stdlibs/std/emit_event_test.go +++ b/gnovm/stdlibs/std/emit_event_test.go @@ -124,8 +124,14 @@ func TestEmit(t *testing.T) { m.Context = ExecContext{EventLogger: elgs} if tt.expectPanic { - X_emit(m, tt.eventType, tt.attrs) - assert.NotNil(t, m.Exception) + assert.Panics(t, func() { + X_emit(m, tt.eventType, tt.attrs) + }) + // X_emit() should m.Panic(), but it should not + // set m.Exception. That happens after m.Run() + // recovers and then calls m.pushPanic(). + // But stdlib should NOT call m.pushPanic(). + assert.Nil(t, m.Exception) } else { X_emit(m, tt.eventType, tt.attrs) assert.Equal(t, len(tt.expectedEvents), len(elgs.Events())) diff --git a/gnovm/stdlibs/std/frame.gno b/gnovm/stdlibs/std/frame.gno index bcffa458043..9d6f2e489f4 100644 --- a/gnovm/stdlibs/std/frame.gno +++ b/gnovm/stdlibs/std/frame.gno @@ -5,6 +5,14 @@ type Realm struct { pkgPath string } +func (r Realm) String() string { + if r.pkgPath == "" { + return "UserRealm{ " + r.addr.String() + " }" + } else { + return "CodeRealm{ " + r.addr.String() + ", " + r.pkgPath + " }" + } +} + func (r Realm) Address() Address { return r.addr } diff --git a/gnovm/stdlibs/std/native.go b/gnovm/stdlibs/std/native.go index 0b17a54f0b4..5c7dfc27b13 100644 --- a/gnovm/stdlibs/std/native.go +++ b/gnovm/stdlibs/std/native.go @@ -91,7 +91,7 @@ func X_getRealm(m *gno.Machine, height int) (address, pkgPath string) { } // Sanity check - if !fr.DidCross { + if !fr.DidCrossing { panic(fmt.Sprintf( "call to cross(fn) did not call crossing : %s", fr.Func.String())) diff --git a/gnovm/tests/files/access2.gno b/gnovm/tests/files/access2.gno index 10ab2ffc352..48c66d1837b 100644 --- a/gnovm/tests/files/access2.gno +++ b/gnovm/tests/files/access2.gno @@ -13,4 +13,4 @@ func main() { } // Error: -// cannot modify external-realm or non-realm object +// cannot directly modify readonly tainted object (w/o method): (const (ref(gno.land/p/demo/testutils) package{})).TestVar1 diff --git a/gnovm/tests/files/addressable_1a_err.gno b/gnovm/tests/files/addressable_1a_err.gno index 366c8082bbf..ff52cc5d5c4 100644 --- a/gnovm/tests/files/addressable_1a_err.gno +++ b/gnovm/tests/files/addressable_1a_err.gno @@ -4,8 +4,5 @@ func main() { _ = &[1]int{1}[0] } -// Error: -// main/files/addressable_1a_err.gno:4:6: cannot take address of [(const (1 int))](const-type int){(const (1 int))}[(const (0 int))] - // TypeCheckError: // main/files/addressable_1a_err.gno:4:7: invalid operation: cannot take address of [1]int{…}[0] (value of type int) diff --git a/gnovm/tests/files/addressable_1b_err.gno b/gnovm/tests/files/addressable_1b_err.gno index dc76d167779..b625f553117 100644 --- a/gnovm/tests/files/addressable_1b_err.gno +++ b/gnovm/tests/files/addressable_1b_err.gno @@ -4,8 +4,5 @@ func main() { _ = [1]int{1}[:] } -// Error: -// main/files/addressable_1b_err.gno:4:6: cannot take address of [(const (1 int))](const-type int){(const (1 int))} - // TypeCheckError: // main/files/addressable_1b_err.gno:4:6: invalid operation: cannot slice [1]int{…} (value of type [1]int) (value not addressable) diff --git a/gnovm/tests/files/addressable_1c_rrr.gno b/gnovm/tests/files/addressable_1c_rrr.gno index 16c1714a15b..fd22e3f2276 100644 --- a/gnovm/tests/files/addressable_1c_rrr.gno +++ b/gnovm/tests/files/addressable_1c_rrr.gno @@ -9,8 +9,5 @@ func getArr() [1]int { return arr } -// Error: -// main/files/addressable_1c_err.gno:4:6: cannot take address of getArr()[(const (0 int))] - // TypeCheckError: // main/files/addressable_1c_rrr.gno:4:7: invalid operation: cannot take address of getArr()[0] (value of type int) diff --git a/gnovm/tests/files/addressable_1d_err.gno b/gnovm/tests/files/addressable_1d_err.gno index a27fbfb6f0a..be3c2b380a1 100644 --- a/gnovm/tests/files/addressable_1d_err.gno +++ b/gnovm/tests/files/addressable_1d_err.gno @@ -8,8 +8,5 @@ func getArr() [1]int { return [1]int{1} } -// Error: -// main/files/addressable_1d_err.gno:4:6: cannot take address of getArr() - // TypeCheckError: // main/files/addressable_1d_err.gno:4:6: invalid operation: cannot slice getArr() (value of type [1]int) (value not addressable) diff --git a/gnovm/tests/files/addressable_3a_err.gno b/gnovm/tests/files/addressable_3a_err.gno index f4e3cd4f639..70a83dbe9ba 100644 --- a/gnovm/tests/files/addressable_3a_err.gno +++ b/gnovm/tests/files/addressable_3a_err.gno @@ -10,8 +10,5 @@ func main() { _ = &S{i: 4}.i } -// Error: -// main/files/addressable_3a_err.gno:10:6: cannot take address of S{i: (const (4 int))}.i - // TypeCheckError: // main/files/addressable_3a_err.gno:10:7: invalid operation: cannot take address of S{…}.i (value of type int) diff --git a/gnovm/tests/files/addressable_3b_err.gno b/gnovm/tests/files/addressable_3b_err.gno index c26c1856952..3b15a86efae 100644 --- a/gnovm/tests/files/addressable_3b_err.gno +++ b/gnovm/tests/files/addressable_3b_err.gno @@ -12,8 +12,5 @@ func getStruct() S { return S{i: 9} } -// Error: -// main/files/addressable_3b_err.gno:8:6: cannot take address of getStruct().i - // TypeCheckError: // main/files/addressable_3b_err.gno:8:7: invalid operation: cannot take address of getStruct().i (value of type int) diff --git a/gnovm/tests/files/addressable_3c_err.gno b/gnovm/tests/files/addressable_3c_err.gno index 3f78d23680a..637876386ae 100644 --- a/gnovm/tests/files/addressable_3c_err.gno +++ b/gnovm/tests/files/addressable_3c_err.gno @@ -13,8 +13,5 @@ func main() { _ = &makeT().Mp } -// Error: -// main/files/addressable_3c_err.gno:13:6: cannot take address of makeT().Mp - // TypeCheckError: // main/files/addressable_3c_err.gno:13:7: invalid operation: cannot take address of makeT().Mp (value of type *int) diff --git a/gnovm/tests/files/addressable_4a_err.gno b/gnovm/tests/files/addressable_4a_err.gno index cdc87892510..e088329c7eb 100644 --- a/gnovm/tests/files/addressable_4a_err.gno +++ b/gnovm/tests/files/addressable_4a_err.gno @@ -5,8 +5,5 @@ func main() { _ = &greeting[2] } -// Error: -// main/files/addressable_4a_err.gno:5:6: cannot take address of greeting[(const (2 int))] - // TypeCheckError: // main/files/addressable_4a_err.gno:5:7: invalid operation: cannot take address of greeting[2] (value of type byte) diff --git a/gnovm/tests/files/addressable_6b_err.gno b/gnovm/tests/files/addressable_6b_err.gno index a2b4e99f34d..e75515b98f7 100644 --- a/gnovm/tests/files/addressable_6b_err.gno +++ b/gnovm/tests/files/addressable_6b_err.gno @@ -10,11 +10,6 @@ func main() { println(&i.(S).a) } -// Error: -// main/files/addressable_6b_err.gno:10:10: cannot take address of i.(S).a - - - // Output: // &(9 int) diff --git a/gnovm/tests/files/addressable_6c_err.gno b/gnovm/tests/files/addressable_6c_err.gno index 14dd5050850..27c9ea2e21f 100644 --- a/gnovm/tests/files/addressable_6c_err.gno +++ b/gnovm/tests/files/addressable_6c_err.gno @@ -6,9 +6,6 @@ func main() { println(&i.([1]int)[0]) } -// Error: -// main/files/addressable_6c_err.gno:6:10: cannot take address of i.([(const (1 int))](const-type int))[(const (0 int))] - // Output: // &(1 int) diff --git a/gnovm/tests/files/addressable_9a_err.gno b/gnovm/tests/files/addressable_9a_err.gno index b8e010b2df7..dc2af72c314 100644 --- a/gnovm/tests/files/addressable_9a_err.gno +++ b/gnovm/tests/files/addressable_9a_err.gno @@ -6,11 +6,6 @@ func main() { println(&m[4]) } -// Error: -// main/files/addressable_9a_err.gno:6:10: cannot take address of m[(const (4 int))] - - - // Output: // &(5 int) diff --git a/gnovm/tests/files/addressable_9b_err.gno b/gnovm/tests/files/addressable_9b_err.gno index 9de5293cefb..e8b8bad51ef 100644 --- a/gnovm/tests/files/addressable_9b_err.gno +++ b/gnovm/tests/files/addressable_9b_err.gno @@ -11,11 +11,6 @@ func main() { println(&mmm[3][3].i) } -// Error: -// main/files/addressable_9b_err.gno:11:10: cannot take address of mmm[(const (3 int))][(const (3 int))].i - - - // Output: // &(7 int) diff --git a/gnovm/tests/files/alloc_0.gno b/gnovm/tests/files/alloc_0.gno index f4a3e0a94f4..5b3fb2662b7 100644 --- a/gnovm/tests/files/alloc_0.gno +++ b/gnovm/tests/files/alloc_0.gno @@ -18,7 +18,5 @@ func main() { // Output: // MemStats: Allocator{maxBytes:100000000, bytes:6358} - - // TypeCheckError: // main/files/alloc_0.gno:13:2: declared and not used: f1 diff --git a/gnovm/tests/files/alloc_1.gno b/gnovm/tests/files/alloc_1.gno index d536ba59393..90f6d1b7824 100644 --- a/gnovm/tests/files/alloc_1.gno +++ b/gnovm/tests/files/alloc_1.gno @@ -23,7 +23,5 @@ func main() { // Output: // MemStats: Allocator{maxBytes:100000000, bytes:7608} - - // TypeCheckError: // main/files/alloc_1.gno:18:2: declared and not used: S1 diff --git a/gnovm/tests/files/alloc_3.gno b/gnovm/tests/files/alloc_3.gno index 2db8f3ebae4..473bdd17b0d 100644 --- a/gnovm/tests/files/alloc_3.gno +++ b/gnovm/tests/files/alloc_3.gno @@ -13,7 +13,5 @@ func main() { // Output: // MemStats after GC: Allocator{maxBytes:110000000, bytes:5704} - - // TypeCheckError: // main/files/alloc_3.gno:7:2: declared and not used: data diff --git a/gnovm/tests/files/alloc_5.gno b/gnovm/tests/files/alloc_5.gno index e999a7e79b3..06689499924 100644 --- a/gnovm/tests/files/alloc_5.gno +++ b/gnovm/tests/files/alloc_5.gno @@ -21,7 +21,5 @@ func main() { // Output: // memstats in main after GC: Allocator{maxBytes:100000000, bytes:6048} - - // TypeCheckError: // main/files/alloc_5.gno:7:2: declared and not used: data diff --git a/gnovm/tests/files/alloc_6.gno b/gnovm/tests/files/alloc_6.gno index 280dddbb434..8ed11220473 100644 --- a/gnovm/tests/files/alloc_6.gno +++ b/gnovm/tests/files/alloc_6.gno @@ -15,7 +15,5 @@ func main() { // Output: // memstats in main after GC: Allocator{maxBytes:100000000, bytes:6048} - - // TypeCheckError: // main/files/alloc_6.gno:7:6: declared and not used: a diff --git a/gnovm/tests/files/alloc_6a.gno b/gnovm/tests/files/alloc_6a.gno index 2a1a39aaf75..aa34b938bc6 100644 --- a/gnovm/tests/files/alloc_6a.gno +++ b/gnovm/tests/files/alloc_6a.gno @@ -17,7 +17,5 @@ func main() { // Output: // memstats in main after GC: Allocator{maxBytes:100000000, bytes:6554} - - // TypeCheckError: // main/files/alloc_6a.gno:8:7: declared and not used: a diff --git a/gnovm/tests/files/alloc_7.gno b/gnovm/tests/files/alloc_7.gno index fd626e5a838..a4607f15700 100644 --- a/gnovm/tests/files/alloc_7.gno +++ b/gnovm/tests/files/alloc_7.gno @@ -15,7 +15,5 @@ func main() { // Output: // MemStats: Allocator{maxBytes:100000000, bytes:5888} - - // TypeCheckError: // main/files/alloc_7.gno:10:2: declared and not used: s1 diff --git a/gnovm/tests/files/append5.gno b/gnovm/tests/files/append5.gno index 0eba5a46463..b1fdae852b1 100644 --- a/gnovm/tests/files/append5.gno +++ b/gnovm/tests/files/append5.gno @@ -7,4 +7,4 @@ func main() { } // Output: -// X \ No newline at end of file +// X diff --git a/gnovm/tests/files/assign0b.gno b/gnovm/tests/files/assign0b.gno index 42faa57634d..0179fce54d4 100644 --- a/gnovm/tests/files/assign0b.gno +++ b/gnovm/tests/files/assign0b.gno @@ -14,6 +14,5 @@ func main() { fmt.Println(http.DefaultClient) } -// Output: -// &{ 10s} -// &{ 0s} +// Error: +// cannot directly modify readonly tainted object (w/o method): (const (ref(github.com/gnolang/gno/_test/net/http) package{})).DefaultClient.Timeout diff --git a/gnovm/tests/files/assign29_native.gno b/gnovm/tests/files/assign29_native.gno index bcca382bafc..04f385d4b00 100644 --- a/gnovm/tests/files/assign29_native.gno +++ b/gnovm/tests/files/assign29_native.gno @@ -11,7 +11,7 @@ func main() { } // Error: -// main/files/assign29_native.gno:8:2: cannot assign to time.Now (neither addressable nor a map index expression) +// cannot directly modify readonly tainted object (w/o method): (const (ref(time) package{})).Now // TypeCheckError: // main/files/assign29_native.gno:8:2: use of package time not in selector; main/files/assign29_native.gno:8:2: cannot assign to time.Now (neither addressable nor a map index expression) diff --git a/gnovm/tests/files/assign34.gno b/gnovm/tests/files/assign34.gno index a289c602028..eebec5b7548 100644 --- a/gnovm/tests/files/assign34.gno +++ b/gnovm/tests/files/assign34.gno @@ -11,4 +11,3 @@ func main() { // Output: // 1 true - diff --git a/gnovm/tests/files/assign_unnamed_type/more/cross_realm_compositelit_filetest.gno b/gnovm/tests/files/assign_unnamed_type/more/cross_realm_compositelit_filetest.gno index 2b6e85caef1..20f3d6d6376 100644 --- a/gnovm/tests/files/assign_unnamed_type/more/cross_realm_compositelit_filetest.gno +++ b/gnovm/tests/files/assign_unnamed_type/more/cross_realm_compositelit_filetest.gno @@ -12,4 +12,4 @@ func main() { } // Output: -// (slice[(0 gno.land/r/demo/tests.Word)] gno.land/r/demo/tests.nat) +// (readonly(slice[(0 gno.land/r/demo/tests.Word)]) gno.land/r/demo/tests.nat) diff --git a/gnovm/tests/files/closure9.gno b/gnovm/tests/files/closure9.gno index 72ca8704077..41b5a16b2b0 100644 --- a/gnovm/tests/files/closure9.gno +++ b/gnovm/tests/files/closure9.gno @@ -21,8 +21,6 @@ func makeClosure(a int) (b int, c func(bool)) { // Preprocessed: // file{ package main; import fmt fmt; var p (const-type int); func main() { b, x := makeClosure((const (1 int))); x((const (true bool))) }; func makeClosure(a~ (const-type int)) b~ (const-type int), c~ func(.arg_0 (const-type bool)) { return (const (2 int)), func func(recurse (const-type bool)){ (const (ref(fmt) package{})).Println(a<~VPBlock(1,1)>, b<~VPBlock(1,2)>, (const (ref(main) package{})).p); if recurse { c<~VPBlock(2,3)>((const (false bool))) } }, b<()~VPBlock(1,1)>, c<()~VPBlock(1,2)>> } } - - // Output: // 1 2 0 // 1 2 0 diff --git a/gnovm/tests/files/composite18.gno b/gnovm/tests/files/composite18.gno index f8f5675234a..218e8a5df9e 100644 --- a/gnovm/tests/files/composite18.gno +++ b/gnovm/tests/files/composite18.gno @@ -10,4 +10,3 @@ func main() { // Output: // map[0:20] - diff --git a/gnovm/tests/files/const39.gno b/gnovm/tests/files/const39.gno index f180cb669a9..b843c5b36c6 100644 --- a/gnovm/tests/files/const39.gno +++ b/gnovm/tests/files/const39.gno @@ -13,7 +13,5 @@ func main() { // Error: // main/files/const39.gno:10:8: typeval{main.T}.Mv (variable of type func(main.T, int) int) is not constant - - // TypeCheckError: // main/files/const39.gno:10:12: T.Mv (value of type func(tv T, a int) int) is not constant diff --git a/gnovm/tests/files/const42.gno b/gnovm/tests/files/const42.gno index 17843d5cfcf..58a3987f8c8 100644 --- a/gnovm/tests/files/const42.gno +++ b/gnovm/tests/files/const42.gno @@ -4,8 +4,5 @@ func main() { const t int } -// Error: -// main/files/const42.gno:4:8: missing init expr for t - // TypeCheckError: // main/files/const42.gno:4:8: missing init expr for t diff --git a/gnovm/tests/files/const45_b.gno b/gnovm/tests/files/const45_b.gno index de235999591..df01afcfda4 100644 --- a/gnovm/tests/files/const45_b.gno +++ b/gnovm/tests/files/const45_b.gno @@ -13,7 +13,5 @@ func main() { // Error: // main/files/const45_b.gno:7:7: typeval{main.MyStruct}{arr: [](const-type int){(const (1 int)), (const (2 int))}}.arr (variable of type []int) is not constant - - // TypeCheckError: // main/files/const45_b.gno:7:11: len(MyStruct{…}.arr) (value of type int) is not constant diff --git a/gnovm/tests/files/goto_empty_stmt.gno b/gnovm/tests/files/goto_empty_stmt.gno index fd939de1045..b11eddd756f 100644 --- a/gnovm/tests/files/goto_empty_stmt.gno +++ b/gnovm/tests/files/goto_empty_stmt.gno @@ -7,4 +7,4 @@ done: } // Output: -// Hi \ No newline at end of file +// Hi diff --git a/gnovm/tests/files/heap_alloc_forloop1a.gno b/gnovm/tests/files/heap_alloc_forloop1a.gno index a2d4ca6890e..9692b47a84d 100644 --- a/gnovm/tests/files/heap_alloc_forloop1a.gno +++ b/gnovm/tests/files/heap_alloc_forloop1a.gno @@ -31,8 +31,6 @@ func main() { // Preprocessed: // file{ package main; import fmt fmt; type Int (const-type main.Int); var s1 []*(typeval{main.Int}); func inc2(j *(typeval{main.Int})) { *(j) = *(j) + (const (2 main.Int)) }; func forLoopRef() { defer func func(){ for i, e := range (const (ref(main) package{})).s1 { (const (ref(fmt) package{})).Printf((const ("s1[%d] is: %d\n" string)), i, *(e)) } }(); for i := (const (0 main.Int)); i<~VPBlock(1,0)> < (const (10 main.Int)); inc2(&(i<~VPBlock(1,0)>)) { s1<~VPBlock(4,1)> = (const (append func([]*main.Int, ...*main.Int) []*main.Int))(s1<~VPBlock(4,1)>, &(i<~VPBlock(1,0)>)) } }; func main() { forLoopRef() } } - - // Output: // s1[0] is: 10 // s1[1] is: 10 diff --git a/gnovm/tests/files/heap_alloc_forloop3.gno b/gnovm/tests/files/heap_alloc_forloop3.gno index a5b7ad5c2ab..58a5054e747 100644 --- a/gnovm/tests/files/heap_alloc_forloop3.gno +++ b/gnovm/tests/files/heap_alloc_forloop3.gno @@ -27,8 +27,6 @@ func main() { // Preprocessed: // file{ package main; type f (const-type main.f); var fs []typeval{main.f}; func forLoopClosure() { defer func func(){ for _, f := range (const (ref(main) package{})).fs { f() } }(); for i := (const (0 int)); i < (const (3 int)); i++ { z := i; fs<~VPBlock(4,1)> = (const (append func([]main.f, ...main.f) []main.f))(fs<~VPBlock(4,1)>, (const-type main.f)(func func(){ (const (println func(...interface {})))(z<~VPBlock(1,0)>) }>)) } }; func main() { forLoopClosure() } } - - // Output: // 0 // 1 diff --git a/gnovm/tests/files/heap_alloc_forloop3a.gno b/gnovm/tests/files/heap_alloc_forloop3a.gno index fde49bec058..ee7f17407fd 100644 --- a/gnovm/tests/files/heap_alloc_forloop3a.gno +++ b/gnovm/tests/files/heap_alloc_forloop3a.gno @@ -29,8 +29,6 @@ func main() { // Preprocessed: // file{ package main; type f (const-type main.f); var fs []typeval{main.f}; func forLoopClosure() { defer func func(){ for _, f := range (const (ref(main) package{})).fs { f() } }(); for i := (const (0 int)); i < (const (3 int)); i++ { x := i; (const (println func(...interface {})))(x); z := i; fs<~VPBlock(4,1)> = (const (append func([]main.f, ...main.f) []main.f))(fs<~VPBlock(4,1)>, (const-type main.f)(func func(){ (const (println func(...interface {})))(z<~VPBlock(1,0)>) }>)) } }; func main() { forLoopClosure() } } - - // Output: // 0 // 1 diff --git a/gnovm/tests/files/heap_alloc_forloop4.gno b/gnovm/tests/files/heap_alloc_forloop4.gno index f61dfe7f2e5..75a23244b38 100644 --- a/gnovm/tests/files/heap_alloc_forloop4.gno +++ b/gnovm/tests/files/heap_alloc_forloop4.gno @@ -25,8 +25,6 @@ func main() { // Preprocessed: // file{ package main; type f (const-type main.f); var fs []typeval{main.f}; func forLoopClosure() { defer func func(){ for _, f := range (const (ref(main) package{})).fs { f() } }(); for i := (const (0 int)); i<~VPBlock(1,0)> < (const (3 int)); i<~VPBlock(1,0)>++ { fs<~VPBlock(4,1)> = (const (append func([]main.f, ...main.f) []main.f))(fs<~VPBlock(4,1)>, (const-type main.f)(func func(){ (const (println func(...interface {})))(i<~VPBlock(1,0)>) }>)) } }; func main() { forLoopClosure() } } - - // Output: // 3 // 3 diff --git a/gnovm/tests/files/heap_alloc_forloop5.gno b/gnovm/tests/files/heap_alloc_forloop5.gno index ef9be0a76ed..772e9ced90e 100644 --- a/gnovm/tests/files/heap_alloc_forloop5.gno +++ b/gnovm/tests/files/heap_alloc_forloop5.gno @@ -26,8 +26,6 @@ func main() { // Preprocessed: // file{ package main; type f (const-type main.f); var fs []typeval{main.f}; func forLoopClosure() { defer func func(){ for _, f := range (const (ref(main) package{})).fs { f() } }(); for i := (const (0 int)); i<~VPBlock(1,0)> < (const (3 int)); i<~VPBlock(1,0)>++ { fs<~VPBlock(4,1)> = (const (append func([]main.f, ...main.f) []main.f))(fs<~VPBlock(4,1)>, (const-type main.f)(func func(){ z := i<~VPBlock(1,1)>; (const (println func(...interface {})))(z) }>)) } }; func main() { forLoopClosure() } } - - // Output: // 3 // 3 diff --git a/gnovm/tests/files/heap_alloc_forloop5a.gno b/gnovm/tests/files/heap_alloc_forloop5a.gno index b2f10bd56c9..db3178e8d3d 100644 --- a/gnovm/tests/files/heap_alloc_forloop5a.gno +++ b/gnovm/tests/files/heap_alloc_forloop5a.gno @@ -27,8 +27,6 @@ func main() { // Preprocessed: // file{ package main; type f (const-type main.f); var fs []typeval{main.f}; func forLoopClosure() { defer func func(){ for _, f := range (const (ref(main) package{})).fs { f() } }(); for i := (const (0 int)); i < (const (3 int)); i++ { x := i; fs<~VPBlock(4,1)> = (const (append func([]main.f, ...main.f) []main.f))(fs<~VPBlock(4,1)>, (const-type main.f)(func func(){ z := x<~VPBlock(1,1)>; (const (println func(...interface {})))(z) }>)) } }; func main() { forLoopClosure() } } - - // Output: // 0 // 1 diff --git a/gnovm/tests/files/map1.gno b/gnovm/tests/files/map1.gno index 6a74fffb56f..ad9fb502df8 100644 --- a/gnovm/tests/files/map1.gno +++ b/gnovm/tests/files/map1.gno @@ -10,8 +10,5 @@ func main() { // the resulting map has only one item (the last one). } -// Error: -// duplicate key struct{} in map literal - // TypeCheckError: // main/files/map1.gno:5:2: declared and not used: m diff --git a/gnovm/tests/files/panic1.gno b/gnovm/tests/files/panic1.gno index cad90aace68..91e127c7200 100644 --- a/gnovm/tests/files/panic1.gno +++ b/gnovm/tests/files/panic1.gno @@ -27,8 +27,6 @@ func main() { // main() // main/files/panic1.gno:22 - - // Error: // here diff --git a/gnovm/tests/files/parse_err2.gno b/gnovm/tests/files/parse_err2.gno index 3e0f79d6935..9c711cff1fe 100644 --- a/gnovm/tests/files/parse_err2.gno +++ b/gnovm/tests/files/parse_err2.gno @@ -1,5 +1,5 @@ +// PKGPATH: math // https://github.com/gnolang/gno/issues/3751 - package math import "testing" diff --git a/gnovm/tests/files/recover1b.gno b/gnovm/tests/files/recover1b.gno index 43a91921b10..992be389e18 100644 --- a/gnovm/tests/files/recover1b.gno +++ b/gnovm/tests/files/recover1b.gno @@ -17,8 +17,6 @@ func main() { // main() // main/files/recover1b.gno:6 - - // Error: // other panic diff --git a/gnovm/tests/files/recursive1a.gno b/gnovm/tests/files/recursive1a.gno index 87681e1fcdd..b3b5496f04d 100644 --- a/gnovm/tests/files/recursive1a.gno +++ b/gnovm/tests/files/recursive1a.gno @@ -12,4 +12,4 @@ func main() { } // Output: -// true \ No newline at end of file +// true diff --git a/gnovm/tests/files/std10.gno b/gnovm/tests/files/std10.gno index 89a838711e4..4225571393d 100644 --- a/gnovm/tests/files/std10.gno +++ b/gnovm/tests/files/std10.gno @@ -11,5 +11,5 @@ func main() { } // Output: -// (struct{("g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm" std.Address),("" string)} std.Realm) +// UserRealm{ g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm } // undefined diff --git a/gnovm/tests/files/std4.gno b/gnovm/tests/files/std4.gno index 6fc4464b1a8..bf37861c217 100644 --- a/gnovm/tests/files/std4.gno +++ b/gnovm/tests/files/std4.gno @@ -10,4 +10,4 @@ func main() { } // Output: -// (struct{("g17rgsdnfxzza0sdfsdma37sdwxagsz378833ca4" std.Address),("main" string)} std.Realm) +// CodeRealm{ g17rgsdnfxzza0sdfsdma37sdwxagsz378833ca4, main } diff --git a/gnovm/tests/files/std5.gno b/gnovm/tests/files/std5.gno index 573468ed823..7f840af87af 100644 --- a/gnovm/tests/files/std5.gno +++ b/gnovm/tests/files/std5.gno @@ -12,5 +12,5 @@ func main() { } // Output: -// (struct{("g17rgsdnfxzza0sdfsdma37sdwxagsz378833ca4" std.Address),("main" string)} std.Realm) -// (struct{("g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm" std.Address),("" string)} std.Realm) +// CodeRealm{ g17rgsdnfxzza0sdfsdma37sdwxagsz378833ca4, main } +// UserRealm{ g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm } diff --git a/gnovm/tests/files/std6.gno b/gnovm/tests/files/std6.gno index 985697d3f68..d7bcac1c040 100644 --- a/gnovm/tests/files/std6.gno +++ b/gnovm/tests/files/std6.gno @@ -14,5 +14,5 @@ func main() { } // Output: -// (struct{("g17rgsdnfxzza0sdfsdma37sdwxagsz378833ca4" std.Address),("main" string)} std.Realm) -// (struct{("g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm" std.Address),("" string)} std.Realm) +// CodeRealm{ g17rgsdnfxzza0sdfsdma37sdwxagsz378833ca4, main } +// UserRealm{ g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm } diff --git a/gnovm/tests/files/std7.gno b/gnovm/tests/files/std7.gno index c68272e35c3..c1556ae94f8 100644 --- a/gnovm/tests/files/std7.gno +++ b/gnovm/tests/files/std7.gno @@ -18,10 +18,5 @@ func main() { } // Output: -// (struct{("g17rgsdnfxzza0sdfsdma37sdwxagsz378833ca4" std.Address),("main" string)} std.Realm) -// (struct{("g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm" std.Address),("" string)} std.Realm) - - - -// Error: -// frame not found +// CodeRealm{ g17rgsdnfxzza0sdfsdma37sdwxagsz378833ca4, main } +// UserRealm{ g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm } diff --git a/gnovm/tests/files/std8.gno b/gnovm/tests/files/std8.gno index 3bcbd352857..c1556ae94f8 100644 --- a/gnovm/tests/files/std8.gno +++ b/gnovm/tests/files/std8.gno @@ -18,15 +18,5 @@ func main() { } // Output: -// (struct{("g17rgsdnfxzza0sdfsdma37sdwxagsz378833ca4" std.Address),("main" string)} std.Realm) -// (struct{("g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm" std.Address),("" string)} std.Realm) - - - -// Stacktrace: -// - - - -// Error: -// frame not found +// CodeRealm{ g17rgsdnfxzza0sdfsdma37sdwxagsz378833ca4, main } +// UserRealm{ g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm } diff --git a/gnovm/tests/files/stdlibs.gno b/gnovm/tests/files/stdlibs.gno index 2fbfa01cec7..eb24b62b620 100644 --- a/gnovm/tests/files/stdlibs.gno +++ b/gnovm/tests/files/stdlibs.gno @@ -13,4 +13,4 @@ func main() { } // Error: -// cannot modify external-realm or non-realm object +// cannot directly modify readonly tainted object (w/o method): (const (ref(time) package{})).UTC diff --git a/gnovm/tests/files/stdlibs_native.gno b/gnovm/tests/files/stdlibs_native.gno index 3afd9f81519..5934c43e3c5 100644 --- a/gnovm/tests/files/stdlibs_native.gno +++ b/gnovm/tests/files/stdlibs_native.gno @@ -11,5 +11,6 @@ func main() { // Output: // false -// true -// done + +// Error: +// cannot directly modify readonly tainted object (w/o method): (const (ref(time) package{})).UTC diff --git a/gnovm/tests/files/var28.gno b/gnovm/tests/files/var28.gno index 279f60168a4..0d0d7685429 100644 --- a/gnovm/tests/files/var28.gno +++ b/gnovm/tests/files/var28.gno @@ -15,4 +15,4 @@ var a1 = func() int { return int(x) } -type b1 int \ No newline at end of file +type b1 int diff --git a/gnovm/tests/files/var29.gno b/gnovm/tests/files/var29.gno index a37ccddd240..f26c230de0d 100644 --- a/gnovm/tests/files/var29.gno +++ b/gnovm/tests/files/var29.gno @@ -11,4 +11,4 @@ var a, b, c = 1, a + 1, b + 1 // Output: // 1 // 2 -// 3 \ No newline at end of file +// 3 diff --git a/gnovm/tests/files/var30.gno b/gnovm/tests/files/var30.gno index c8c9efabdef..1aec4792fcb 100644 --- a/gnovm/tests/files/var30.gno +++ b/gnovm/tests/files/var30.gno @@ -14,4 +14,4 @@ var d = a // 1 // 2 // 3 -// 1 \ No newline at end of file +// 1 diff --git a/gnovm/tests/files/zrealm0.gno b/gnovm/tests/files/zrealm0.gno index 2a4d7a90d50..480a76212ef 100644 --- a/gnovm/tests/files/zrealm0.gno +++ b/gnovm/tests/files/zrealm0.gno @@ -15,6 +15,10 @@ func main() { // undefined // 1 + + + + // The below tests that the realm's block (of 1 variable) changed. The first // element image in the package (block) is for the "main" function, which // appears first because function declarations are defined in a file before diff --git a/gnovm/tests/files/zrealm_borrow0.gno b/gnovm/tests/files/zrealm_borrow0.gno index a618ea67d41..4099fc76244 100644 --- a/gnovm/tests/files/zrealm_borrow0.gno +++ b/gnovm/tests/files/zrealm_borrow0.gno @@ -33,7 +33,7 @@ func (s *Struct) printRealms() { // below is called. // Should this be allowed? // Whether it panics or not is determined by - // 'fr.DidCross = true' in PushFrameCall. + // 'fr.DidCrossing = true' in PushFrameCall. crossing() fmt.Println(std.CurrentRealm()) diff --git a/gnovm/tests/files/zrealm_borrow1.gno b/gnovm/tests/files/zrealm_borrow1.gno index dfbfdd47035..e57828ed3d9 100644 --- a/gnovm/tests/files/zrealm_borrow1.gno +++ b/gnovm/tests/files/zrealm_borrow1.gno @@ -75,5 +75,5 @@ func main() { // Output: // 101 -// {g1evmgnh6rmk8vgahd4p2pc3reh0dw26twaz3rqq gno.land/r/borrow_test} -// {g1evmgnh6rmk8vgahd4p2pc3reh0dw26twaz3rqq gno.land/r/borrow_test} +// CodeRealm{ g1evmgnh6rmk8vgahd4p2pc3reh0dw26twaz3rqq, gno.land/r/borrow_test } +// CodeRealm{ g1evmgnh6rmk8vgahd4p2pc3reh0dw26twaz3rqq, gno.land/r/borrow_test } diff --git a/gnovm/tests/files/zrealm_borrow2.gno b/gnovm/tests/files/zrealm_borrow2.gno index 65570f9bb6e..59ce8572b89 100644 --- a/gnovm/tests/files/zrealm_borrow2.gno +++ b/gnovm/tests/files/zrealm_borrow2.gno @@ -73,5 +73,5 @@ func main() { // Output: // 101 -// {g1evmgnh6rmk8vgahd4p2pc3reh0dw26twaz3rqq gno.land/r/borrow_test} -// {g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm } +// CodeRealm{ g1evmgnh6rmk8vgahd4p2pc3reh0dw26twaz3rqq, gno.land/r/borrow_test } +// UserRealm{ g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm } diff --git a/gnovm/tests/files/zrealm_crosspanic0.gno b/gnovm/tests/files/zrealm_crosspanic0.gno new file mode 100644 index 00000000000..07dee181a9e --- /dev/null +++ b/gnovm/tests/files/zrealm_crosspanic0.gno @@ -0,0 +1,48 @@ +// PKGPATH: gno.land/r/crossrealm_test +package crossrealm_test + +// This tests that panics that cross realm boundaries abort the transaction. + +import ( + "gno.land/r/demo/tests/crossrealm_b" +) + +type StructA struct { + A any +} + +type StructB struct { + B *StructA +} + +func (sb *StructB) Set(sa *StructA) { + sb.B = sa +} + +func (sb *StructB) Panic() { + panic("success: this panic should surface") +} + +func init() { + // save StructB{} in crossrealm_b. + sb := &StructB{} + cross(crossrealm_b.SetObject)(sb) +} + +func main() { + sb := crossrealm_b.GetObject().(*StructB) + //sa := &StructA{} + //sb.Set(sa) // attach sa to crossrealm_b. + + func() { + defer func() { + r := recover() + println("fail: should not be printing anything but recovered", r) + panic("fail: the transaction should have been aborted; this defer should not even be called") + }() + sb.Panic() + }() +} + +// Error: +// success: this panic should surface diff --git a/gnovm/tests/files/zrealm_crossrealm10.gno b/gnovm/tests/files/zrealm_crossrealm10.gno index 4693d3cce76..da9d9aaac70 100644 --- a/gnovm/tests/files/zrealm_crossrealm10.gno +++ b/gnovm/tests/files/zrealm_crossrealm10.gno @@ -13,4 +13,4 @@ func main() { } // Error: -// cannot modify external-realm or non-realm object +// cannot directly modify readonly tainted object (w/o method): SomeValue3<~VPBlock(3,5)>.Field diff --git a/gnovm/tests/files/zrealm_crossrealm13.gno b/gnovm/tests/files/zrealm_crossrealm13.gno index 293a327e6e7..ca13d9a7c19 100644 --- a/gnovm/tests/files/zrealm_crossrealm13.gno +++ b/gnovm/tests/files/zrealm_crossrealm13.gno @@ -45,17 +45,17 @@ func PrintRealm() { // Output: // From main: -// PR() CurrentRealm: (struct{("g17rgsdnfxzza0sdfsdma37sdwxagsz378833ca4" std.Address),("main" string)} std.Realm) -// PR() PreviousRealm: (struct{("g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm" std.Address),("" string)} std.Realm) -// CurrentRealm: (struct{("g17rgsdnfxzza0sdfsdma37sdwxagsz378833ca4" std.Address),("main" string)} std.Realm) -// PreviousRealm: (struct{("g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm" std.Address),("" string)} std.Realm) +// PR() CurrentRealm: CodeRealm{ g17rgsdnfxzza0sdfsdma37sdwxagsz378833ca4, main } +// PR() PreviousRealm: UserRealm{ g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm } +// CurrentRealm: CodeRealm{ g17rgsdnfxzza0sdfsdma37sdwxagsz378833ca4, main } +// PreviousRealm: UserRealm{ g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm } // // From g1user origin: -// CurrentRealm: (struct{("g1user" std.Address),("" string)} std.Realm) +// CurrentRealm: UserRealm{ g1user } // PreviousRealm: frame not found: cannot seek beyond origin caller override // // From gno.land/r/sys/users realm: -// PR() CurrentRealm: (struct{("g1njxh4leja7h52ea0lnq9crx3j6782g77nc7yd4" std.Address),("gno.land/r/sys/users" string)} std.Realm) -// PR() PreviousRealm: (struct{("g17rgsdnfxzza0sdfsdma37sdwxagsz378833ca4" std.Address),("main" string)} std.Realm) -// CurrentRealm: (struct{("g1njxh4leja7h52ea0lnq9crx3j6782g77nc7yd4" std.Address),("gno.land/r/sys/users" string)} std.Realm) -// PreviousRealm: (struct{("g17rgsdnfxzza0sdfsdma37sdwxagsz378833ca4" std.Address),("main" string)} std.Realm) +// PR() CurrentRealm: CodeRealm{ g1njxh4leja7h52ea0lnq9crx3j6782g77nc7yd4, gno.land/r/sys/users } +// PR() PreviousRealm: CodeRealm{ g17rgsdnfxzza0sdfsdma37sdwxagsz378833ca4, main } +// CurrentRealm: CodeRealm{ g1njxh4leja7h52ea0lnq9crx3j6782g77nc7yd4, gno.land/r/sys/users } +// PreviousRealm: CodeRealm{ g17rgsdnfxzza0sdfsdma37sdwxagsz378833ca4, main } diff --git a/gnovm/tests/files/zrealm_crossrealm13a.gno b/gnovm/tests/files/zrealm_crossrealm13a.gno index bf250baece4..0c5e86763fb 100644 --- a/gnovm/tests/files/zrealm_crossrealm13a.gno +++ b/gnovm/tests/files/zrealm_crossrealm13a.gno @@ -52,21 +52,21 @@ func PrintRealm() { // Output: // From main: -// PrintRealm: CurrentRealm: (struct{("g1r0mlnkc05z0fv49km99z60qnp95tengyqfdr02" std.Address),("gno.land/r/demo/groups" string)} std.Realm) -// PrintRealm: PreviousRealm: (struct{("g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm" std.Address),("" string)} std.Realm) -// PrintRealm: CurrentRealm: (struct{("g1r0mlnkc05z0fv49km99z60qnp95tengyqfdr02" std.Address),("gno.land/r/demo/groups" string)} std.Realm) -// PrintRealm: PreviousRealm: (struct{("g1r0mlnkc05z0fv49km99z60qnp95tengyqfdr02" std.Address),("gno.land/r/demo/groups" string)} std.Realm) -// CurrentRealm: (struct{("g1r0mlnkc05z0fv49km99z60qnp95tengyqfdr02" std.Address),("gno.land/r/demo/groups" string)} std.Realm) -// PreviousRealm: (struct{("g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm" std.Address),("" string)} std.Realm) +// PrintRealm: CurrentRealm: CodeRealm{ g1r0mlnkc05z0fv49km99z60qnp95tengyqfdr02, gno.land/r/demo/groups } +// PrintRealm: PreviousRealm: UserRealm{ g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm } +// PrintRealm: CurrentRealm: CodeRealm{ g1r0mlnkc05z0fv49km99z60qnp95tengyqfdr02, gno.land/r/demo/groups } +// PrintRealm: PreviousRealm: CodeRealm{ g1r0mlnkc05z0fv49km99z60qnp95tengyqfdr02, gno.land/r/demo/groups } +// CurrentRealm: CodeRealm{ g1r0mlnkc05z0fv49km99z60qnp95tengyqfdr02, gno.land/r/demo/groups } +// PreviousRealm: UserRealm{ g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm } // // From g1user origin: -// PrintRealm: CurrentRealm: (struct{("g1r0mlnkc05z0fv49km99z60qnp95tengyqfdr02" std.Address),("gno.land/r/demo/groups" string)} std.Realm) -// PrintRealm: PreviousRealm: (struct{("g1user" std.Address),("" string)} std.Realm) -// CurrentRealm: (struct{("g1user" std.Address),("" string)} std.Realm) +// PrintRealm: CurrentRealm: CodeRealm{ g1r0mlnkc05z0fv49km99z60qnp95tengyqfdr02, gno.land/r/demo/groups } +// PrintRealm: PreviousRealm: UserRealm{ g1user } +// CurrentRealm: UserRealm{ g1user } // PreviousRealm: frame not found: cannot seek beyond origin caller override // // From gno.land/r/sys/users realm: -// PrintRealm: CurrentRealm: (struct{("g1r0mlnkc05z0fv49km99z60qnp95tengyqfdr02" std.Address),("gno.land/r/demo/groups" string)} std.Realm) -// PrintRealm: PreviousRealm: (struct{("g1njxh4leja7h52ea0lnq9crx3j6782g77nc7yd4" std.Address),("gno.land/r/sys/users" string)} std.Realm) -// CurrentRealm: (struct{("g1njxh4leja7h52ea0lnq9crx3j6782g77nc7yd4" std.Address),("gno.land/r/sys/users" string)} std.Realm) -// PreviousRealm: (struct{("g1r0mlnkc05z0fv49km99z60qnp95tengyqfdr02" std.Address),("gno.land/r/demo/groups" string)} std.Realm) +// PrintRealm: CurrentRealm: CodeRealm{ g1r0mlnkc05z0fv49km99z60qnp95tengyqfdr02, gno.land/r/demo/groups } +// PrintRealm: PreviousRealm: CodeRealm{ g1njxh4leja7h52ea0lnq9crx3j6782g77nc7yd4, gno.land/r/sys/users } +// CurrentRealm: CodeRealm{ g1njxh4leja7h52ea0lnq9crx3j6782g77nc7yd4, gno.land/r/sys/users } +// PreviousRealm: CodeRealm{ g1r0mlnkc05z0fv49km99z60qnp95tengyqfdr02, gno.land/r/demo/groups } diff --git a/gnovm/tests/files/zrealm_crossrealm2.gno b/gnovm/tests/files/zrealm_crossrealm2.gno index ea0ab417adc..96cc19317ab 100644 --- a/gnovm/tests/files/zrealm_crossrealm2.gno +++ b/gnovm/tests/files/zrealm_crossrealm2.gno @@ -21,4 +21,4 @@ func main() { } // Error: -// cannot modify external-realm or non-realm object +// cannot directly modify readonly tainted object (w/o method): t.Field diff --git a/gnovm/tests/files/zrealm_crossrealm21.gno b/gnovm/tests/files/zrealm_crossrealm21.gno index 8ab268cd5ac..01bd22fb082 100644 --- a/gnovm/tests/files/zrealm_crossrealm21.gno +++ b/gnovm/tests/files/zrealm_crossrealm21.gno @@ -22,8 +22,6 @@ func main() { // hello B cur=gno.land/r/demo/tests/crossrealm_b prev=gno.land/r/demo/tests/crossrealm // . - - // Realm: // finalizerealm["gno.land/r/demo/tests/crossrealm"] // u[1712ac7adcfdc8e58a67e5615e20fb312394c4df:7]= diff --git a/gnovm/tests/files/zrealm_crossrealm22.gno b/gnovm/tests/files/zrealm_crossrealm22.gno index 3e9008e97c4..3c1789b9ed1 100644 --- a/gnovm/tests/files/zrealm_crossrealm22.gno +++ b/gnovm/tests/files/zrealm_crossrealm22.gno @@ -36,8 +36,6 @@ func main() { // hello D cur=gno.land/r/demo/tests/crossrealm_b prev=gno.land/r/demo/tests/crossrealm // . - - // Realm: // finalizerealm["gno.land/r/demo/tests/crossrealm"] // c[1712ac7adcfdc8e58a67e5615e20fb312394c4df:37]={ diff --git a/gnovm/tests/files/zrealm_crossrealm26.gno b/gnovm/tests/files/zrealm_crossrealm26.gno index c932836179f..1a58aaac9f4 100644 --- a/gnovm/tests/files/zrealm_crossrealm26.gno +++ b/gnovm/tests/files/zrealm_crossrealm26.gno @@ -19,4 +19,4 @@ func main() { } // Error: -// cannot modify external-realm or non-realm object +// cannot directly modify readonly tainted object (w/o method): s<~VPBlock(1,0)>.A diff --git a/gnovm/tests/files/zrealm_crossrealm27.gno b/gnovm/tests/files/zrealm_crossrealm27.gno index 7931f2241bd..658db899b21 100644 --- a/gnovm/tests/files/zrealm_crossrealm27.gno +++ b/gnovm/tests/files/zrealm_crossrealm27.gno @@ -25,9 +25,7 @@ func main() { } // Error: -// cannot modify external-realm or non-realm object - - +// cannot directly modify readonly tainted object (w/o method): s<~VPBlock(3,1)>.A // Preprocessed: // file{ package crossrealm_test; import crossrealm_b gno.land/r/demo/tests/crossrealm_b; type Struct (const-type gno.land/r/crossrealm_test.Struct); var s *(typeval{gno.land/r/crossrealm_test.Struct}); func init.2() { s2 := &(typeval{gno.land/r/crossrealm_test.Struct}{A: (const (100 int))}); (const (cross func(func(interface {})) func(interface {})))((const (ref(gno.land/r/demo/tests/crossrealm_b) package{})).SetObject)(func func(){ (const (println func(...interface {})))(&(s2<~VPBlock(1,0)>.A)) }>); s<~VPBlock(3,1)> = s2<~VPBlock(1,0)> }; func main() { (const (crossing func()))(); s<~VPBlock(3,1)>.A = (const (123 int)); (const (println func(...interface {})))(s<~VPBlock(3,1)>) } } diff --git a/gnovm/tests/files/zrealm_crossrealm28.gno b/gnovm/tests/files/zrealm_crossrealm28.gno index 8c0ff2f70d9..17ee77d4059 100644 --- a/gnovm/tests/files/zrealm_crossrealm28.gno +++ b/gnovm/tests/files/zrealm_crossrealm28.gno @@ -28,13 +28,9 @@ func main() { // Output: // &(struct{(123 int)} gno.land/r/crossrealm_test.Struct) - - // Preprocessed: // file{ package crossrealm_test; import crossrealm_b gno.land/r/demo/tests/crossrealm_b; type Struct (const-type gno.land/r/crossrealm_test.Struct); var s *(typeval{gno.land/r/crossrealm_test.Struct}); func init.2() { s<~VPBlock(3,1)> = &(typeval{gno.land/r/crossrealm_test.Struct}{A: (const (100 int))}); (const (cross func(func(interface {})) func(interface {})))((const (ref(gno.land/r/demo/tests/crossrealm_b) package{})).SetObject)(func func(){ (const (println func(...interface {})))(&((const (ref(gno.land/r/crossrealm_test) package{})).s.A)) }) }; func main() { (const (crossing func()))(); (const (cross func(func(interface {})) func(interface {})))((const (ref(gno.land/r/demo/tests/crossrealm_b) package{})).SetObject)((const (123 int))); s<~VPBlock(3,1)>.A = (const (123 int)); (const (println func(...interface {})))(s<~VPBlock(3,1)>) } } - - // Realm: // finalizerealm["gno.land/r/demo/tests/crossrealm_b"] // u[0edc46caf30c00efd87b6c272673239eafbd051e:13]= diff --git a/gnovm/tests/files/zrealm_crossrealm29.gno b/gnovm/tests/files/zrealm_crossrealm29.gno index be573d81300..91ba976d7cb 100644 --- a/gnovm/tests/files/zrealm_crossrealm29.gno +++ b/gnovm/tests/files/zrealm_crossrealm29.gno @@ -31,7 +31,5 @@ func main() { // Output: // &(struct{(123 int)} gno.land/r/crossrealm_test.Struct) - - // Preprocessed: // file{ package crossrealm_test; import crossrealm_b gno.land/r/demo/tests/crossrealm_b; type Struct (const-type gno.land/r/crossrealm_test.Struct); var s *(typeval{gno.land/r/crossrealm_test.Struct}); func init.2() { s<~VPBlock(3,1)> = &(typeval{gno.land/r/crossrealm_test.Struct}{A: (const (100 int))}); (const (cross func(func(interface {})) func(interface {})))((const (ref(gno.land/r/demo/tests/crossrealm_b) package{})).SetObject)(func func(){ (const (println func(...interface {})))(&((const (ref(gno.land/r/crossrealm_test) package{})).s.A)) }) }; func main() { (const (crossing func()))(); s<~VPBlock(3,1)>.A = (const (123 int)); (const (println func(...interface {})))(s<~VPBlock(3,1)>) } } diff --git a/gnovm/tests/files/zrealm_crossrealm30.gno b/gnovm/tests/files/zrealm_crossrealm30.gno new file mode 100644 index 00000000000..684fe0bf00d --- /dev/null +++ b/gnovm/tests/files/zrealm_crossrealm30.gno @@ -0,0 +1,42 @@ +// PKGPATH: gno.land/r/crossrealm_test +package crossrealm_test + +// This tests for realm finalization upon implicit (borrow) realm switches. +// This is like zrealm_crossrealm30[b,c].gno, but with `return` for doOpReturnFromBlock. +// Assuming there are no defers: +// `return x` calls doOpReturn or doOpReturnAfterCopy if func results are named, while +// `return` calls doOpReturnFromBlock. + +import ( + "gno.land/r/demo/tests/crossrealm_b" +) + +type StructA struct { + A any +} + +type StructB struct { + B *StructA +} + +func (sb *StructB) Set(sa *StructA) { + sb.B = sa +} + +func init() { + // save StructB{} in crossrealm_b. + sb := &StructB{} + cross(crossrealm_b.SetObject)(sb) +} + +func main() { + sb := crossrealm_b.GetObject().(*StructB) + sa := &StructA{} + sb.Set(sa) // attach sa to crossrealm_b. + + sa.A = 1 // should fail + panic("sa.A should not be mutable; it should be attached to crossrealm_b.") +} + +// Error: +// cannot directly modify readonly tainted object (w/o method): sa.A diff --git a/gnovm/tests/files/zrealm_crossrealm30b.gno b/gnovm/tests/files/zrealm_crossrealm30b.gno new file mode 100644 index 00000000000..cfb7d74920e --- /dev/null +++ b/gnovm/tests/files/zrealm_crossrealm30b.gno @@ -0,0 +1,43 @@ +// PKGPATH: gno.land/r/crossrealm_test +package crossrealm_test + +// This tests for realm finalization upon implicit (borrow) realm switches. +// This is like zrealm_crossrealm30.gno, but with `return x` for doOpReturn. +// Assuming there are no defers: +// `return x` calls doOpReturn or doOpReturnAfterCopy if func results are named, while +// `return` calls doOpReturnFromBlock. + +import ( + "gno.land/r/demo/tests/crossrealm_b" +) + +type StructA struct { + A any +} + +type StructB struct { + B *StructA +} + +func (sb *StructB) Set(sa *StructA) struct{} { + sb.B = sa + return struct{}{} +} + +func init() { + // save StructB{} in crossrealm_b. + sb := &StructB{} + cross(crossrealm_b.SetObject)(sb) +} + +func main() { + sb := crossrealm_b.GetObject().(*StructB) + sa := &StructA{} + sb.Set(sa) // attach sa to crossrealm_b. + + sa.A = 1 // should fail + panic("sa.A should not be mutable; it should be attached to crossrealm_b.") +} + +// Error: +// cannot directly modify readonly tainted object (w/o method): sa.A diff --git a/gnovm/tests/files/zrealm_crossrealm30c.gno b/gnovm/tests/files/zrealm_crossrealm30c.gno new file mode 100644 index 00000000000..35e1018f6c4 --- /dev/null +++ b/gnovm/tests/files/zrealm_crossrealm30c.gno @@ -0,0 +1,48 @@ +// PKGPATH: gno.land/r/crossrealm_test +package crossrealm_test + +// This tests for realm finalization upon implicit (borrow) realm switches. +// This is like zrealm_crossrealm30.gno, but with `return x` for doOpReturnAfterCopy. +// Assuming there are no defers: +// `return x` calls doOpReturn or doOpReturnAfterCopy if func results are named, while +// `return` calls doOpReturnFromBlock. + +import ( + "gno.land/r/demo/tests/crossrealm_b" +) + +type StructA struct { + A any +} + +type StructB struct { + B *StructA +} + +func (sb *StructB) Set(sa *StructA) (r struct{}) { // <-- the named result names it doOpReturnAfterCopy. + defer func() { // <-- the defer also ensures doOpReturnAfterCopy. + if r != struct{}{} { + panic("should not happen") + } + }() + sb.B = sa + return struct{}{} +} + +func init() { + // save StructB{} in crossrealm_b. + sb := &StructB{} + cross(crossrealm_b.SetObject)(sb) +} + +func main() { + sb := crossrealm_b.GetObject().(*StructB) + sa := &StructA{} + sb.Set(sa) // attach sa to crossrealm_b. + + sa.A = 1 // should fail + panic("sa.A should not be mutable; it should be attached to crossrealm_b.") +} + +// Error: +// cannot directly modify readonly tainted object (w/o method): sa.A diff --git a/gnovm/tests/files/zrealm_crossrealm30d.gno b/gnovm/tests/files/zrealm_crossrealm30d.gno new file mode 100644 index 00000000000..2aebcc224f2 --- /dev/null +++ b/gnovm/tests/files/zrealm_crossrealm30d.gno @@ -0,0 +1,49 @@ +// PKGPATH: gno.land/r/crossrealm_test +package crossrealm_test + +// This tests for realm finalization upon implicit (borrow) realm switches. +// This is like zrealm_crossrealm30[a,b,c].gno, but with cross(). +// Unlike a,b,c, this file saves sb locally, but crosses into it(self). + +import ( + "gno.land/r/demo/tests/crossrealm_b" +) + +type StructA struct { + A any +} + +type StructB struct { + B *StructA +} + +func (sb *StructB) Set(sa *StructA) struct{} { + crossing() + + sb.B = sa + return struct{}{} +} + +var sb *StructB + +func init() { + sb = &StructB{} +} + +func main() { + sa := &StructA{} + cross(sb.Set)(sa) // attach sa now. + + // If sa was attached to sb (this realm), + // it would not attach to crossrealm_b below, + // so we wouldn't be able to mutate it directly. + cross(crossrealm_b.SetObject)(sb) + sa.A = 1 // should succeed + if sa.A != 1 { + panic("expected sa.A to be 1") + } + println("Success: sa direct manipulation succeeded, meaning it was attached by cross(sb.Set)(sa).") +} + +// Output: +// Success: sa direct manipulation succeeded, meaning it was attached by cross(sb.Set)(sa). diff --git a/gnovm/tests/files/zrealm_crossrealm4.gno b/gnovm/tests/files/zrealm_crossrealm4.gno index a28af50e9e5..79196297a8b 100644 --- a/gnovm/tests/files/zrealm_crossrealm4.gno +++ b/gnovm/tests/files/zrealm_crossrealm4.gno @@ -21,4 +21,4 @@ func main() { } // Output: -// &(struct{("_modified" string)} gno.land/r/demo/tests.TestRealmObject) +// readonly(&(struct{("_modified" string)} gno.land/r/demo/tests.TestRealmObject)) diff --git a/gnovm/tests/files/zrealm_crossrealm5.gno b/gnovm/tests/files/zrealm_crossrealm5.gno index b78acc9969c..93a294b1d30 100644 --- a/gnovm/tests/files/zrealm_crossrealm5.gno +++ b/gnovm/tests/files/zrealm_crossrealm5.gno @@ -21,4 +21,4 @@ func main() { } // Error: -// cannot modify external-realm or non-realm object +// cannot directly modify readonly tainted object (w/o method): somevalue<~VPBlock(3,0)>.Field diff --git a/gnovm/tests/files/zrealm_crossrealm7.gno b/gnovm/tests/files/zrealm_crossrealm7.gno index 1ac062ff56e..c1e28daf06c 100644 --- a/gnovm/tests/files/zrealm_crossrealm7.gno +++ b/gnovm/tests/files/zrealm_crossrealm7.gno @@ -13,4 +13,4 @@ func main() { } // Error: -// cannot modify external-realm or non-realm object +// cannot directly modify readonly tainted object (w/o method): somevalue1<~VPBlock(3,3)>.Field diff --git a/gnovm/tests/files/zrealm_crossrealm8.gno b/gnovm/tests/files/zrealm_crossrealm8.gno index 1decf6f6728..a5438882626 100644 --- a/gnovm/tests/files/zrealm_crossrealm8.gno +++ b/gnovm/tests/files/zrealm_crossrealm8.gno @@ -13,4 +13,4 @@ func main() { } // Error: -// cannot modify external-realm or non-realm object +// cannot directly modify readonly tainted object (w/o method): SomeValue2<~VPBlock(3,4)>.Field diff --git a/gnovm/tests/files/zrealm_crossrealm9.gno b/gnovm/tests/files/zrealm_crossrealm9.gno index dce0cf22bad..591afb470d5 100644 --- a/gnovm/tests/files/zrealm_crossrealm9.gno +++ b/gnovm/tests/files/zrealm_crossrealm9.gno @@ -13,4 +13,4 @@ func main() { } // Error: -// cannot modify external-realm or non-realm object +// cannot directly modify readonly tainted object (w/o method): (const (ref(gno.land/p/demo/tests) package{})).SomeValue2.Field diff --git a/gnovm/tests/files/zrealm_initctx.gno b/gnovm/tests/files/zrealm_initctx.gno index fea2e00e536..db48a7b9262 100644 --- a/gnovm/tests/files/zrealm_initctx.gno +++ b/gnovm/tests/files/zrealm_initctx.gno @@ -22,11 +22,13 @@ func main() { // XXX consider panic instead println(addr) println(addrInit) + println(".") // trailing newline is needed. } // Output: // // +// . // Realm: // finalizerealm["gno.land/r/demo/tests_test"] diff --git a/gnovm/tests/files/zrealm_natbind0.gno b/gnovm/tests/files/zrealm_natbind0.gno index 3318280b4b6..e12f8014119 100644 --- a/gnovm/tests/files/zrealm_natbind0.gno +++ b/gnovm/tests/files/zrealm_natbind0.gno @@ -28,8 +28,6 @@ func main() { // 123 // dev - - // Realm: // finalizerealm["gno.land/r/test"] // u[a8ada09dee16d791fd406d629fe29bb0ed084a30:3]= diff --git a/gnovm/tests/files/zrealm_no_borrow.gno b/gnovm/tests/files/zrealm_no_borrow.gno index 38e175c22cb..51f0d4d1822 100644 --- a/gnovm/tests/files/zrealm_no_borrow.gno +++ b/gnovm/tests/files/zrealm_no_borrow.gno @@ -45,5 +45,5 @@ func main() { // Output: // s.A: 101 -// current realm: {g1evmgnh6rmk8vgahd4p2pc3reh0dw26twaz3rqq gno.land/r/borrow_test} -// previous realm: {g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm } +// current realm: CodeRealm{ g1evmgnh6rmk8vgahd4p2pc3reh0dw26twaz3rqq, gno.land/r/borrow_test } +// previous realm: UserRealm{ g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm } diff --git a/gnovm/tests/files/zrealm_panic.gno b/gnovm/tests/files/zrealm_panic.gno index c7a9ddd6f8d..12078b33a80 100644 --- a/gnovm/tests/files/zrealm_panic.gno +++ b/gnovm/tests/files/zrealm_panic.gno @@ -17,8 +17,6 @@ func main() { // Error: // panic - - // Stacktrace: // panic: panic // ms.Panic() diff --git a/gnovm/tests/files/zrealm_tests0.gno b/gnovm/tests/files/zrealm_tests0.gno index 949ad02d15a..35a5aafb0bf 100644 --- a/gnovm/tests/files/zrealm_tests0.gno +++ b/gnovm/tests/files/zrealm_tests0.gno @@ -182,4 +182,7 @@ func main() { // "RefCount": "1" // }, // d[0ffe7732b4d549b4cf9ec18bd68641cd2c75ad0a:54] +// finalizerealm["gno.land/r/demo/tests"] +// finalizerealm["gno.land/r/demo/tests"] +// finalizerealm["gno.land/r/demo/tests"] // finalizerealm["gno.land/r/demo/tests_test"] diff --git a/gnovm/tests/stdlibs/std/std.go b/gnovm/tests/stdlibs/std/std.go index 27524ab2b12..52a725092b8 100644 --- a/gnovm/tests/stdlibs/std/std.go +++ b/gnovm/tests/stdlibs/std/std.go @@ -128,7 +128,7 @@ func X_getRealm(m *gno.Machine, height int) (address string, pkgPath string) { // Sanity check XXX move check elsewhere if !overridden { - if !fr.DidCross { + if !fr.DidCrossing { panic(fmt.Sprintf( "cross(fn) but fn didn't call crossing(): %s.%s", fr.Func.PkgPath, diff --git a/tm2/pkg/crypto/crypto.go b/tm2/pkg/crypto/crypto.go index 7908a082d3b..1b9b8fea224 100644 --- a/tm2/pkg/crypto/crypto.go +++ b/tm2/pkg/crypto/crypto.go @@ -44,7 +44,8 @@ func MustAddressFromString(str string) (addr Address) { } func AddressFromPreimage(bz []byte) Address { - return AddressFromBytes(tmhash.SumTruncated(bz)) + addr := AddressFromBytes(tmhash.SumTruncated(bz)) + return addr } func AddressFromBytes(bz []byte) (ret Address) {