Skip to content

Commit

Permalink
feat(stdlibs/std)!: change TestSetPrevRealm to TestSetRealm (#2164)
Browse files Browse the repository at this point in the history
This PR removes the `TestSetPrevRealm` and `TestSetPrevAddr`, added in
#890, in favour of a new API `TestSetRealm`, which is more flexible in
its behaviour.

The function works by setting the result of `CurrentRealm()` for the
frame in which the `TestSetRealm` function is called. Consequently,
calling `PrevRealm` in any function called within will yield the value
passed to `TestSetRealm`.

`TestSetRealm` allows to transparently set "user" and "code" realms, by
using two new costructors in the testing stdlibs, `std.NewUserRealm` and
`std.NewCodeRealm`, allowing emulation of both kinds of callers.

The call is not `TestSetPrevRealm` as it does not permanently "set" the
value returned by PrevRealm, as if in a mock; it simply changes the
Realm of the current frame, allowing "deep" calls to PrevRealm() to
correctly return the value of the previous realm.


<details><summary>Contributors' checklist...</summary>

- [ ] Added new tests, or not needed, or not feasible
- [ ] Provided an example (e.g. screenshot) to aid review or the PR is
self-explanatory
- [ ] Updated the official documentation or not needed
- [ ] No breaking changes were made, or a `BREAKING CHANGE: xxx` message
was included in the description
- [ ] Added references to related issues and PRs
- [ ] Provided any useful hints for running manual tests
- [ ] Added new benchmarks to [generated
graphs](https://gnoland.github.io/benchmarks), if any. More info
[here](https://github.com/gnolang/gno/blob/master/.benchmarks/README.md).
</details>
  • Loading branch information
thehowl authored May 29, 2024
1 parent e02b751 commit 380d7bf
Show file tree
Hide file tree
Showing 18 changed files with 297 additions and 128 deletions.
6 changes: 0 additions & 6 deletions examples/gno.land/r/x/test_prev/gno.mod

This file was deleted.

18 changes: 0 additions & 18 deletions examples/gno.land/r/x/test_prev/test_prev.gno

This file was deleted.

32 changes: 0 additions & 32 deletions examples/gno.land/r/x/test_prev/test_prev_test.gno

This file was deleted.

16 changes: 8 additions & 8 deletions gnovm/pkg/gnolang/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -2058,19 +2058,19 @@ func (m *Machine) String() string {
builder.WriteString(fmt.Sprintf(" #%d %v\n", i, m.Values[i]))
}

builder.WriteString(`Exprs: `)
builder.WriteString(" Exprs:\n")

for i := len(m.Exprs) - 1; i >= 0; i-- {
builder.WriteString(fmt.Sprintf(" #%d %v\n", i, m.Exprs[i]))
}

builder.WriteString(`Stmts: `)
builder.WriteString(" Stmts:\n")

for i := len(m.Stmts) - 1; i >= 0; i-- {
builder.WriteString(fmt.Sprintf(" #%d %v\n", i, m.Stmts[i]))
}

builder.WriteString(`Blocks: `)
builder.WriteString(" Blocks:\n")

for b := m.LastBlock(); b != nil; {
gen := builder.Len()/3 + 1
Expand Down Expand Up @@ -2107,7 +2107,7 @@ func (m *Machine) String() string {
}
}

builder.WriteString(`Blocks (other): `)
builder.WriteString(" Blocks (other):\n")

for i := len(m.Blocks) - 2; i >= 0; i-- {
b := m.Blocks[i]
Expand All @@ -2119,17 +2119,17 @@ func (m *Machine) String() string {
if _, ok := b.Source.(*PackageNode); ok {
break // done, skip *PackageNode.
} else {
builder.WriteString(fmt.Sprintf(" #%d %s", i,
builder.WriteString(fmt.Sprintf(" #%d %s\n", i,
b.StringIndented(" ")))
if b.Source != nil {
sb := b.GetSource(m.Store).GetStaticBlock().GetBlock()
builder.WriteString(fmt.Sprintf(" (static) #%d %s", i,
builder.WriteString(fmt.Sprintf(" (static) #%d %s\n", i,
sb.StringIndented(" ")))
}
}
}

builder.WriteString(`Frames: `)
builder.WriteString(" Frames:\n")

for i := len(m.Frames) - 1; i >= 0; i-- {
builder.WriteString(fmt.Sprintf(" #%d %s\n", i, m.Frames[i]))
Expand All @@ -2139,7 +2139,7 @@ func (m *Machine) String() string {
builder.WriteString(fmt.Sprintf(" Realm:\n %s\n", m.Realm.Path))
}

builder.WriteString(`Exceptions: `)
builder.WriteString(" Exceptions:\n")

for _, ex := range m.Exceptions {
builder.WriteString(fmt.Sprintf(" %s\n", ex.Sprint(m)))
Expand Down
10 changes: 5 additions & 5 deletions gnovm/stdlibs/std/banker.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,14 @@ const (
var reDenom = regexp.MustCompile("[a-z][a-z0-9]{2,15}")

func X_bankerGetCoins(m *gno.Machine, bt uint8, addr string) (denoms []string, amounts []int64) {
coins := m.Context.(ExecContext).Banker.GetCoins(crypto.Bech32Address(addr))
coins := GetContext(m).Banker.GetCoins(crypto.Bech32Address(addr))
return ExpandCoins(coins)
}

func X_bankerSendCoins(m *gno.Machine, bt uint8, fromS, toS string, denoms []string, amounts []int64) {
// bt != BankerTypeReadonly (checked in gno)

ctx := m.Context.(ExecContext)
ctx := GetContext(m)
amt := CompactCoins(denoms, amounts)
from, to := crypto.Bech32Address(fromS), crypto.Bech32Address(toS)

Expand Down Expand Up @@ -87,7 +87,7 @@ func X_bankerSendCoins(m *gno.Machine, bt uint8, fromS, toS string, denoms []str
}

func X_bankerTotalCoin(m *gno.Machine, bt uint8, denom string) int64 {
return m.Context.(ExecContext).Banker.TotalCoin(denom)
return GetContext(m).Banker.TotalCoin(denom)
}

func X_bankerIssueCoin(m *gno.Machine, bt uint8, addr string, denom string, amount int64) {
Expand All @@ -104,7 +104,7 @@ func X_bankerIssueCoin(m *gno.Machine, bt uint8, addr string, denom string, amou
// ibc_denom := 'ibc/' + hash('path' + 'base_denom')
// gno_realm_denom := '/' + 'pkg_path' + ':' + 'base_denom'
newDenom := "/" + m.Realm.Path + ":" + denom
m.Context.(ExecContext).Banker.IssueCoin(crypto.Bech32Address(addr), newDenom, amount)
GetContext(m).Banker.IssueCoin(crypto.Bech32Address(addr), newDenom, amount)
}

func X_bankerRemoveCoin(m *gno.Machine, bt uint8, addr string, denom string, amount int64) {
Expand All @@ -117,5 +117,5 @@ func X_bankerRemoveCoin(m *gno.Machine, bt uint8, addr string, denom string, amo
}

newDenom := "/" + m.Realm.Path + ":" + denom
m.Context.(ExecContext).Banker.RemoveCoin(crypto.Bech32Address(addr), newDenom, amount)
GetContext(m).Banker.RemoveCoin(crypto.Bech32Address(addr), newDenom, amount)
}
26 changes: 26 additions & 0 deletions gnovm/stdlibs/std/context.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package std

import (
gno "github.com/gnolang/gno/gnovm/pkg/gnolang"
"github.com/gnolang/gno/tm2/pkg/crypto"
"github.com/gnolang/gno/tm2/pkg/sdk"
"github.com/gnolang/gno/tm2/pkg/std"
Expand All @@ -19,3 +20,28 @@ type ExecContext struct {
Banker BankerInterface
EventLogger *sdk.EventLogger
}

// GetContext returns the execution context.
// This is used to allow extending the exec context using interfaces,
// for instance when testing.
func (e ExecContext) GetExecContext() ExecContext {
return e
}

var _ ExecContexter = ExecContext{}

// ExecContexter is a type capable of returning the parent [ExecContext]. When
// using these standard libraries, m.Context should always implement this
// interface. This can be obtained by embedding [ExecContext].
type ExecContexter interface {
GetExecContext() ExecContext
}

// NOTE: In order to make this work by simply embedding ExecContext in another
// context (like TestExecContext), the method needs to be something other than
// the field name.

// GetContext returns the context from the Gno machine.
func GetContext(m *gno.Machine) ExecContext {
return m.Context.(ExecContexter).GetExecContext()
}
2 changes: 1 addition & 1 deletion gnovm/stdlibs/std/emit_event.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func X_emit(m *gno.Machine, typ string, attrs []string) {
Func: fnIdent,
Attributes: eventAttrs,
}
ctx := m.Context.(ExecContext)
ctx := GetContext(m)
ctx.EventLogger.EmitEvent(evt)
}

Expand Down
18 changes: 10 additions & 8 deletions gnovm/stdlibs/std/native.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ func IsOriginCall(m *gno.Machine) bool {
}

func GetChainID(m *gno.Machine) string {
return m.Context.(ExecContext).ChainID
return GetContext(m).ChainID
}

func GetHeight(m *gno.Machine) int64 {
return m.Context.(ExecContext).Height
return GetContext(m).Height
}

// getPrevFunctionNameFromTarget returns the last called function name (identifier) from the call stack.
Expand Down Expand Up @@ -63,16 +63,16 @@ func findPrevFuncName(m *gno.Machine, targetIndex int) string {
}

func X_origSend(m *gno.Machine) (denoms []string, amounts []int64) {
os := m.Context.(ExecContext).OrigSend
os := GetContext(m).OrigSend
return ExpandCoins(os)
}

func X_origCaller(m *gno.Machine) string {
return string(m.Context.(ExecContext).OrigCaller)
return string(GetContext(m).OrigCaller)
}

func X_origPkgAddr(m *gno.Machine) string {
return string(m.Context.(ExecContext).OrigPkgAddr)
return string(GetContext(m).OrigPkgAddr)
}

func X_callerAt(m *gno.Machine, n int) string {
Expand All @@ -91,22 +91,24 @@ func X_callerAt(m *gno.Machine, n int) string {
}
if n == m.NumFrames() {
// This makes it consistent with GetOrigCaller.
ctx := m.Context.(ExecContext)
ctx := GetContext(m)
return string(ctx.OrigCaller)
}
return string(m.MustLastCallFrame(n).LastPackage.GetPkgAddr().Bech32())
}

func X_getRealm(m *gno.Machine, height int) (address, pkgPath string) {
// NOTE: keep in sync with test/stdlibs/std.getRealm

var (
ctx = m.Context.(ExecContext)
ctx = GetContext(m)
currentCaller crypto.Bech32Address
// Keeps track of the number of times currentCaller
// has changed.
changes int
)

for i := m.NumFrames() - 1; i > 0; i-- {
for i := m.NumFrames() - 1; i >= 0; i-- {
fr := m.Frames[i]
if fr.LastPackage == nil || !fr.LastPackage.IsRealm() {
continue
Expand Down
4 changes: 4 additions & 0 deletions gnovm/stdlibs/stdlibs.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ import (

type ExecContext = libsstd.ExecContext

func GetContext(m *gno.Machine) ExecContext {
return libsstd.GetContext(m)
}

func NativeStore(pkgPath string, name gno.Name) func(*gno.Machine) {
for _, nf := range nativeFuncs {
if nf.gnoPkg == pkgPath && name == nf.gnoFunc {
Expand Down
2 changes: 1 addition & 1 deletion gnovm/stdlibs/time/time.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ func X_now(m *gno.Machine) (sec int64, nsec int32, mono int64) {
return 0, 0, 0
}

ctx := m.Context.(std.ExecContext)
ctx := std.GetContext(m)
return ctx.Timestamp, int32(ctx.TimestampNano), ctx.Timestamp*int64(time.Second) + ctx.TimestampNano
}
8 changes: 6 additions & 2 deletions gnovm/tests/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (

gno "github.com/gnolang/gno/gnovm/pkg/gnolang"
"github.com/gnolang/gno/gnovm/stdlibs"
teststd "github.com/gnolang/gno/gnovm/tests/stdlibs/std"
"github.com/gnolang/gno/tm2/pkg/crypto"
osm "github.com/gnolang/gno/tm2/pkg/os"
"github.com/gnolang/gno/tm2/pkg/sdk"
Expand Down Expand Up @@ -46,7 +47,7 @@ func testMachineCustom(store gno.Store, pkgPath string, stdout io.Writer, maxAll
return m
}

func testContext(pkgPath string, send std.Coins) stdlibs.ExecContext {
func testContext(pkgPath string, send std.Coins) *teststd.TestExecContext {
// FIXME: create a better package to manage this, with custom constructors
pkgAddr := gno.DerivePkgAddr(pkgPath) // the addr of the pkgPath called.
caller := gno.DerivePkgAddr("user1.gno")
Expand All @@ -65,7 +66,10 @@ func testContext(pkgPath string, send std.Coins) stdlibs.ExecContext {
Banker: banker,
EventLogger: sdk.NewEventLogger(),
}
return ctx
return &teststd.TestExecContext{
ExecContext: ctx,
RealmFrames: make(map[*gno.Frame]teststd.RealmOverride),
}
}

type runFileTestOptions struct {
Expand Down
48 changes: 48 additions & 0 deletions gnovm/tests/files/zrealm_crossrealm13.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package main

import (
"std"
)

func main() {
PrintRealm()
println(pad("CurrentRealm:"), std.CurrentRealm())
println(pad("PrevRealm:"), std.PrevRealm())
std.TestSetRealm(std.NewUserRealm("g1user"))
PrintRealm()
println(pad("CurrentRealm:"), std.CurrentRealm())
println(pad("PrevRealm:"), std.PrevRealm())
std.TestSetRealm(std.NewCodeRealm("gno.land/r/demo/users"))
PrintRealm()
println(pad("CurrentRealm:"), std.CurrentRealm())
println(pad("PrevRealm:"), std.PrevRealm())
}

func pad(s string) string {
for len(s) < 26 {
s += " "
}
return s
}

func PrintRealm() {
println(pad("PrintRealm: CurrentRealm:"), std.CurrentRealm())
println(pad("PrintRealm: PrevRealm:"), std.PrevRealm())
}

// Because this is the context of a package, using PrintRealm()
// should not change the output of the main function.

// Output:
// PrintRealm: CurrentRealm: (struct{("g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm" std.Address),("" string)} std.Realm)
// PrintRealm: PrevRealm: (struct{("g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm" std.Address),("" string)} std.Realm)
// CurrentRealm: (struct{("g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm" std.Address),("" string)} std.Realm)
// PrevRealm: (struct{("g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm" std.Address),("" string)} std.Realm)
// PrintRealm: CurrentRealm: (struct{("g1user" std.Address),("" string)} std.Realm)
// PrintRealm: PrevRealm: (struct{("g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm" std.Address),("" string)} std.Realm)
// CurrentRealm: (struct{("g1user" std.Address),("" string)} std.Realm)
// PrevRealm: (struct{("g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm" std.Address),("" string)} std.Realm)
// PrintRealm: CurrentRealm: (struct{("g17m4ga9t9dxn8uf06p3cahdavzfexe33ecg8v2s" std.Address),("gno.land/r/demo/users" string)} std.Realm)
// PrintRealm: PrevRealm: (struct{("g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm" std.Address),("" string)} std.Realm)
// CurrentRealm: (struct{("g17m4ga9t9dxn8uf06p3cahdavzfexe33ecg8v2s" std.Address),("gno.land/r/demo/users" string)} std.Realm)
// PrevRealm: (struct{("g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm" std.Address),("" string)} std.Realm)
Loading

0 comments on commit 380d7bf

Please sign in to comment.