Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: make AssertOriginCall always panic with MsgRun #1665

Merged
8 changes: 8 additions & 0 deletions examples/gno.land/r/demo/tests/subtests/subtests.gno
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,11 @@ func GetPrevRealm() std.Realm {
func Exec(fn func()) {
fn()
}

func CallAssertOriginCall() {
std.AssertOriginCall()
}

func CallIsOriginCall() bool {
return std.IsOriginCall()
}
12 changes: 10 additions & 2 deletions examples/gno.land/r/demo/tests/tests.gno
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,22 @@ func InitOrigCaller() std.Address {
return initOrigCaller
}

func AssertOriginCall() {
func CallAssertOriginCall() {
std.AssertOriginCall()
}

func IsOriginCall() bool {
func CallIsOriginCall() bool {
return std.IsOriginCall()
}

func CallSubtestsAssertOriginCall() {
rsubtests.CallAssertOriginCall()
}

func CallSubtestsIsOriginCall() bool {
return rsubtests.CallIsOriginCall()
}

//----------------------------------------
// Test structure to ensure cross-realm modification is prevented.

Expand Down
36 changes: 24 additions & 12 deletions examples/gno.land/r/demo/tests/tests_test.gno
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,40 @@ import (
)

func TestAssertOriginCall(t *testing.T) {
// No-panic case
AssertOriginCall()
if !IsOriginCall() {
// CallAssertOriginCall(): no panic
CallAssertOriginCall()
if !CallIsOriginCall() {
t.Errorf("expected IsOriginCall=true but got false")
}

// Panic case
// CallAssertOriginCall() from a block: panic
expectedReason := "invalid non-origin call"
func() {
defer func() {
r := recover()
if r == nil || r.(string) != expectedReason {
t.Errorf("expected panic with '%v', got '%v'", expectedReason, r)
}
}()
// if called inside a function literal, this is no longer an origin call
// because there's one additional frame (the function literal block).
if CallIsOriginCall() {
t.Errorf("expected IsOriginCall=false but got true")
}
CallAssertOriginCall()
}()

// CallSubtestsAssertOriginCall(): panic
defer func() {
r := recover()
if r == nil || r.(string) != expectedReason {
t.Errorf("expected panic with '%v', got '%v'", expectedReason, r)
}
}()
func() {
// if called inside a function literal, this is no longer an origin call
// because there's one additional frame (the function literal).
if IsOriginCall() {
t.Errorf("expected IsOriginCall=false but got true")
}
AssertOriginCall()
}()
if CallSubtestsIsOriginCall() {
t.Errorf("expected IsOriginCall=false but got true")
}
CallSubtestsAssertOriginCall()
}

func TestPrevRealm(t *testing.T) {
Expand Down
28 changes: 14 additions & 14 deletions examples/gno.land/r/demo/tests/z0_filetest.gno
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,24 @@ import (
)

func main() {
println("IsOriginCall:", tests.IsOriginCall())
tests.AssertOriginCall()
println("AssertOriginCall doesn't panic when called directly")
println("tests.CallIsOriginCall:", tests.CallIsOriginCall())
tests.CallAssertOriginCall()
println("tests.CallAssertOriginCall doesn't panic when called directly")

func() {
// if called inside a function literal, this is no longer an origin call
// because there's one additional frame (the function literal).
println("IsOriginCall:", tests.IsOriginCall())
{
// 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())
defer func() {
r := recover()
println("AssertOriginCall panics if when called inside a function literal:", r)
println("tests.AssertOriginCall panics if when called inside a function literal:", r)
}()
tests.AssertOriginCall()
}()
tests.CallAssertOriginCall()
}
}

// Output:
// IsOriginCall: true
// AssertOriginCall doesn't panic when called directly
// IsOriginCall: false
// AssertOriginCall panics if when called inside a function literal: invalid non-origin call
// tests.CallIsOriginCall: true
// tests.CallAssertOriginCall doesn't panic when called directly
// tests.CallIsOriginCall: true
// tests.AssertOriginCall panics if when called inside a function literal: undefined
245 changes: 245 additions & 0 deletions gno.land/cmd/gnoland/testdata/assertorigincall.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
# This test ensures the consistency of the std.AssertOriginCall function, in
# the following situations:
#
# | Num | Msg Type | Call from | Entry Point | Result |
# |-----|:--------:|:-------------------:|:----------------------:|:------:|
# | 1 | MsgCall | wallet direct | myrealm.A() | PANIC |
# | 2 | | | myrealm.B() | pass |
# | 3 | | | myrealm.C() | pass |
# | 4 | | through /r/foo | myrealm.A() | PANIC |
# | 5 | | | myrealm.B() | pass |
# | 6 | | | myrealm.C() | PANIC |
# | 7 | | through /p/demo/bar | myrealm.A() | PANIC |
# | 8 | | | myrealm.B() | pass |
# | 9 | | | myrealm.C() | PANIC |
# | 10 | MsgRun | wallet direct | myrealm.A() | PANIC |
# | 11 | | | myrealm.B() | pass |
# | 12 | | | myrealm.C() | PANIC |
# | 13 | | through /r/foo | myrealm.A() | PANIC |
# | 14 | | | myrealm.B() | pass |
# | 15 | | | myrealm.C() | PANIC |
# | 16 | | through /p/demo/bar | myrealm.A() | PANIC |
# | 17 | | | myrealm.B() | pass |
# | 18 | | | myrealm.C() | PANIC |
# | 19 | MsgCall | wallet direct | std.AssertOriginCall() | pass |
# | 20 | MsgRun | wallet direct | std.AssertOriginCall() | PANIC |

# Init
## set up and start a new node
loadpkg gno.land/r/myrlm $WORK/r/myrlm
loadpkg gno.land/r/foo $WORK/r/foo
loadpkg gno.land/p/demo/bar $WORK/p/demo/bar
gnoland start

# Test cases
## 1. MsgCall -> myrlm.A: PANIC
! gnokey maketx call -pkgpath gno.land/r/myrlm -func A -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1
stderr 'invalid non-origin call'

## 2. MsgCall -> myrlm.B: PASS
gnokey maketx call -pkgpath gno.land/r/myrlm -func B -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1
stdout 'OK!'

## 3. MsgCall -> myrlm.C: PASS
gnokey maketx call -pkgpath gno.land/r/myrlm -func C -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1
stdout 'OK!'

## 4. MsgCall -> r/foo.A -> myrlm.A: PANIC
! gnokey maketx call -pkgpath gno.land/r/foo -func A -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1
stderr 'invalid non-origin call'

## 5. MsgCall -> r/foo.B -> myrlm.B: PASS
gnokey maketx call -pkgpath gno.land/r/foo -func B -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1
stdout 'OK!'

## 6. MsgCall -> r/foo.C -> myrlm.C: PANIC
! gnokey maketx call -pkgpath gno.land/r/foo -func C -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1
stderr 'invalid non-origin call'

## 7. MsgCall -> p/demo/bar.A -> myrlm.A: PANIC
! gnokey maketx call -pkgpath gno.land/p/demo/bar -func A -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1
stderr 'invalid non-origin call'

## 8. MsgCall -> p/demo/bar.B -> myrlm.B: PASS
gnokey maketx call -pkgpath gno.land/p/demo/bar -func B -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1
stdout 'OK!'

## 9. MsgCall -> p/demo/bar.C -> myrlm.C: PANIC
! gnokey maketx call -pkgpath gno.land/p/demo/bar -func C -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1
stderr 'invalid non-origin call'

## 10. MsgRun -> run.main -> myrlm.A: PANIC
! gnokey maketx run -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1 $WORK/run/myrlmA.gno
stderr 'invalid non-origin call'

## 11. MsgRun -> run.main -> myrlm.B: PASS
gnokey maketx run -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1 $WORK/run/myrlmB.gno
stdout 'OK!'

## 12. MsgRun -> run.main -> myrlm.C: PANIC
! gnokey maketx run -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1 $WORK/run/myrlmC.gno
stderr 'invalid non-origin call'

## 13. MsgRun -> run.main -> foo.A: PANIC
! gnokey maketx run -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1 $WORK/run/fooA.gno
stderr 'invalid non-origin call'

## 14. MsgRun -> run.main -> foo.B: PASS
gnokey maketx run -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1 $WORK/run/fooB.gno
stdout 'OK!'

## 15. MsgRun -> run.main -> foo.C: PANIC
! gnokey maketx run -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1 $WORK/run/fooC.gno
stderr 'invalid non-origin call'

## 16. MsgRun -> run.main -> bar.A: PANIC
! gnokey maketx run -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1 $WORK/run/barA.gno
stderr 'invalid non-origin call'

## 17. MsgRun -> run.main -> bar.B: PASS
gnokey maketx run -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1 $WORK/run/barB.gno
stdout 'OK!'

## 18. MsgRun -> run.main -> bar.C: PANIC
! gnokey maketx run -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1 $WORK/run/barC.gno
stderr 'invalid non-origin call'

## 19. MsgCall -> std.AssertOriginCall: pass
gnokey maketx call -pkgpath std -func AssertOriginCall -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1
stdout 'OK!'

## 20. MsgRun -> std.AssertOriginCall: PANIC
! gnokey maketx run -gas-fee 100000ugnot -gas-wanted 2000000 -broadcast -chainid tendermint_test test1 $WORK/run/baz.gno
stderr 'invalid non-origin call'


-- r/myrlm/rlm.gno --
package myrlm

import "std"

func A() {
C()
}

func B() {
if false {
C()
}
}

func C() {
std.AssertOriginCall()
}
-- r/foo/foo.gno --
package foo

import "gno.land/r/myrlm"

func A() {
myrlm.A()
}

func B() {
myrlm.B()
}

func C() {
myrlm.C()
}
-- p/demo/bar/bar.gno --
package bar

import "gno.land/r/myrlm"

func A() {
myrlm.A()
}

func B() {
myrlm.B()
}

func C() {
myrlm.C()
}
-- run/myrlmA.gno --
package main

import myrlm "gno.land/r/myrlm"

func main() {
myrlm.A()
}
-- run/myrlmB.gno --
package main

import "gno.land/r/myrlm"

func main() {
myrlm.B()
}
-- run/myrlmC.gno --
package main

import "gno.land/r/myrlm"

func main() {
myrlm.C()
}
-- run/fooA.gno --
package main

import "gno.land/r/foo"

func main() {
foo.A()
}
-- run/fooB.gno --
package main

import "gno.land/r/foo"

func main() {
foo.B()
}
-- run/fooC.gno --
package main

import "gno.land/r/foo"

func main() {
foo.C()
}
-- run/barA.gno --
package main

import "gno.land/p/demo/bar"

func main() {
bar.A()
}
-- run/barB.gno --
package main

import "gno.land/p/demo/bar"

func main() {
bar.B()
}
-- run/barC.gno --
package main

import "gno.land/p/demo/bar"

func main() {
bar.C()
}
-- run/baz.gno --
package main

import "std"

func main() {
std.AssertOriginCall()
}
10 changes: 9 additions & 1 deletion gnovm/stdlibs/std/native.gno
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
package std

func AssertOriginCall() // injected
// AssertOriginCall panics if [IsOriginCall] returns false.
func AssertOriginCall() // injected

// IsOriginCall returns true only if the calling method is invoked via a direct
// MsgCall. It returns false for all other cases, like if the calling method
// is invoked by another method (even from the same realm or package).
// It also returns false every time when the transaction is broadcasted via
// MsgRun.
func IsOriginCall() bool // injected

func GetChainID() string // injected
func GetHeight() int64 // injected

Expand Down
Loading
Loading