diff --git a/contribs/gnodev/cmd/gnodev/main.go b/contribs/gnodev/cmd/gnodev/main.go index 2c694b608bb..c9d6487d753 100644 --- a/contribs/gnodev/cmd/gnodev/main.go +++ b/contribs/gnodev/cmd/gnodev/main.go @@ -61,18 +61,20 @@ type devCfg struct { webRemoteHelperAddr string // Node Configuration - minimal bool - verbose bool - noWatch bool - noReplay bool - maxGas int64 - chainId string - serverMode bool - unsafeAPI bool + minimal bool + verbose bool + noWatch bool + noReplay bool + maxGas int64 + chainId string + chainDomain string + serverMode bool + unsafeAPI bool } var defaultDevOptions = &devCfg{ chainId: "dev", + chainDomain: "gno.land", maxGas: 10_000_000_000, webListenerAddr: "127.0.0.1:8888", nodeRPCListenerAddr: "127.0.0.1:26657", @@ -203,6 +205,13 @@ func (c *devCfg) RegisterFlags(fs *flag.FlagSet) { "set node ChainID", ) + fs.StringVar( + &c.chainDomain, + "chain-domain", + defaultDevOptions.chainDomain, + "set node ChainDomain", + ) + fs.BoolVar( &c.noWatch, "no-watch", diff --git a/contribs/gnodev/cmd/gnodev/setup_node.go b/contribs/gnodev/cmd/gnodev/setup_node.go index a2b1970d0ef..eaeb89b7e95 100644 --- a/contribs/gnodev/cmd/gnodev/setup_node.go +++ b/contribs/gnodev/cmd/gnodev/setup_node.go @@ -57,7 +57,7 @@ func setupDevNodeConfig( balances gnoland.Balances, pkgspath []gnodev.PackagePath, ) *gnodev.NodeConfig { - config := gnodev.DefaultNodeConfig(cfg.root) + config := gnodev.DefaultNodeConfig(cfg.root, cfg.chainDomain) config.Logger = logger config.Emitter = emitter diff --git a/contribs/gnodev/pkg/dev/node.go b/contribs/gnodev/pkg/dev/node.go index e0ed64aad36..0502c03c86f 100644 --- a/contribs/gnodev/pkg/dev/node.go +++ b/contribs/gnodev/pkg/dev/node.go @@ -43,9 +43,10 @@ type NodeConfig struct { NoReplay bool MaxGasPerBlock int64 ChainID string + ChainDomain string } -func DefaultNodeConfig(rootdir string) *NodeConfig { +func DefaultNodeConfig(rootdir, domain string) *NodeConfig { tmc := gnoland.NewDefaultTMConfig(rootdir) tmc.Consensus.SkipTimeoutCommit = false // avoid time drifting, see issue #1507 tmc.Consensus.WALDisabled = true @@ -65,6 +66,7 @@ func DefaultNodeConfig(rootdir string) *NodeConfig { DefaultDeployer: defaultDeployer, BalancesList: balances, ChainID: tmc.ChainID(), + ChainDomain: domain, TMConfig: tmc, SkipFailingGenesisTxs: true, MaxGasPerBlock: 10_000_000_000, @@ -487,7 +489,7 @@ func (n *Node) rebuildNode(ctx context.Context, genesis gnoland.GnoGenesisState) } // Setup node config - nodeConfig := newNodeConfig(n.config.TMConfig, n.config.ChainID, genesis) + nodeConfig := newNodeConfig(n.config.TMConfig, n.config.ChainID, n.config.ChainDomain, genesis) nodeConfig.GenesisTxResultHandler = n.genesisTxResultHandler // Speed up stdlib loading after first start (saves about 2-3 seconds on each reload). nodeConfig.CacheStdlibLoad = true @@ -566,10 +568,10 @@ func (n *Node) genesisTxResultHandler(ctx sdk.Context, tx std.Tx, res sdk.Result return } -func newNodeConfig(tmc *tmcfg.Config, chainid string, appstate gnoland.GnoGenesisState) *gnoland.InMemoryNodeConfig { +func newNodeConfig(tmc *tmcfg.Config, chainid, chaindomain string, appstate gnoland.GnoGenesisState) *gnoland.InMemoryNodeConfig { // Create Mocked Identity pv := gnoland.NewMockedPrivValidator() - genesis := gnoland.NewDefaultGenesisConfig(chainid) + genesis := gnoland.NewDefaultGenesisConfig(chainid, chaindomain) genesis.AppState = appstate // Add self as validator @@ -583,10 +585,11 @@ func newNodeConfig(tmc *tmcfg.Config, chainid string, appstate gnoland.GnoGenesi }, } - return &gnoland.InMemoryNodeConfig{ + cfg := &gnoland.InMemoryNodeConfig{ PrivValidator: pv, TMConfig: tmc, Genesis: genesis, VMOutput: os.Stdout, } + return cfg } diff --git a/contribs/gnodev/pkg/dev/node_test.go b/contribs/gnodev/pkg/dev/node_test.go index e05e5a996fa..4a4acc232b9 100644 --- a/contribs/gnodev/pkg/dev/node_test.go +++ b/contribs/gnodev/pkg/dev/node_test.go @@ -38,7 +38,7 @@ func TestNewNode_NoPackages(t *testing.T) { logger := log.NewTestingLogger(t) // Call NewDevNode with no package should work - cfg := DefaultNodeConfig(gnoenv.RootDir()) + cfg := DefaultNodeConfig(gnoenv.RootDir(), "gno.land") cfg.Logger = logger node, err := NewDevNode(ctx, cfg) require.NoError(t, err) @@ -66,7 +66,7 @@ func Render(_ string) string { return "foo" } logger := log.NewTestingLogger(t) // Call NewDevNode with no package should work - cfg := DefaultNodeConfig(gnoenv.RootDir()) + cfg := DefaultNodeConfig(gnoenv.RootDir(), "gno.land") cfg.PackagesPathList = []PackagePath{pkgpath} cfg.Logger = logger node, err := NewDevNode(ctx, cfg) @@ -475,7 +475,7 @@ func generateTestingPackage(t *testing.T, nameFile ...string) PackagePath { } func createDefaultTestingNodeConfig(pkgslist ...PackagePath) *NodeConfig { - cfg := DefaultNodeConfig(gnoenv.RootDir()) + cfg := DefaultNodeConfig(gnoenv.RootDir(), "gno.land") cfg.PackagesPathList = pkgslist return cfg } diff --git a/gno.land/cmd/gnoland/testdata/addpkg_domain.txtar b/gno.land/cmd/gnoland/testdata/addpkg_domain.txtar new file mode 100644 index 00000000000..25e4fe0d3a3 --- /dev/null +++ b/gno.land/cmd/gnoland/testdata/addpkg_domain.txtar @@ -0,0 +1,15 @@ +gnoland start + +# addpkg with anotherdomain.land +! gnokey maketx addpkg -pkgdir $WORK -pkgpath anotherdomain.land/r/foobar/bar -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1 +stdout 'TX HASH:' +stderr 'invalid package path' +stderr 'invalid domain: anotherdomain.land/r/foobar/bar' + +# addpkg with gno.land +gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/foobar/bar -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1 +stdout 'OK!' + +-- bar.gno -- +package bar +func Render(path string) string { return "hello" } diff --git a/gno.land/cmd/gnoland/testdata/genesis_params.txtar b/gno.land/cmd/gnoland/testdata/genesis_params.txtar index 43ecd8ccacb..d09ededf78a 100644 --- a/gno.land/cmd/gnoland/testdata/genesis_params.txtar +++ b/gno.land/cmd/gnoland/testdata/genesis_params.txtar @@ -1,14 +1,28 @@ -# test for https://github.com/gnolang/gno/pull/3003 +# Test for #3003, #2911. gnoland start +# Query and validate official parameters. +# These parameters should ideally be tested in a txtar format to ensure that a +# default initialization of "gnoland" provides the expected default values. + +# Verify the default chain domain parameter for Gno.land +gnokey query params/vm/gno.land/r/sys/params.vm.chain_domain.string +stdout 'data: "gno.land"$' + +# Test custom parameters to confirm they return the expected values and types. + gnokey query params/vm/gno.land/r/sys/params.test.foo.string stdout 'data: "bar"$' + gnokey query params/vm/gno.land/r/sys/params.test.foo.int64 stdout 'data: "-1337"' + gnokey query params/vm/gno.land/r/sys/params.test.foo.uint64 stdout 'data: "42"' + gnokey query params/vm/gno.land/r/sys/params.test.foo.bool stdout 'data: true' -# XXX: bytes + +# TODO: Consider adding a test case for a byte array parameter diff --git a/gno.land/cmd/gnoland/testdata/simulate_gas.txtar b/gno.land/cmd/gnoland/testdata/simulate_gas.txtar index cd58b4ccc8f..9d3c8abe69f 100644 --- a/gno.land/cmd/gnoland/testdata/simulate_gas.txtar +++ b/gno.land/cmd/gnoland/testdata/simulate_gas.txtar @@ -6,11 +6,11 @@ gnoland start # simulate only gnokey maketx call -pkgpath gno.land/r/simulate -func Hello -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test -simulate only test1 -stdout 'GAS USED: 50299' +stdout 'GAS USED: 51299' # simulate skip gnokey maketx call -pkgpath gno.land/r/simulate -func Hello -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test -simulate skip test1 -stdout 'GAS USED: 50299' # same as simulate only +stdout 'GAS USED: 51299' # same as simulate only -- package/package.gno -- diff --git a/gno.land/genesis/genesis_params.toml b/gno.land/genesis/genesis_params.toml index 5f4d9c5015c..fb080024624 100644 --- a/gno.land/genesis/genesis_params.toml +++ b/gno.land/genesis/genesis_params.toml @@ -8,7 +8,7 @@ ## gnovm ["gno.land/r/sys/params.vm"] - # TODO: chain_domain.string = "gno.land" + chain_domain.string = "gno.land" # TODO: max_gas.int64 = 100_000_000 # TODO: chain_tz.string = "UTC" # TODO: default_storage_allowance.string = "" diff --git a/gno.land/pkg/gnoland/node_inmemory.go b/gno.land/pkg/gnoland/node_inmemory.go index 426a8c780c7..f42166411c8 100644 --- a/gno.land/pkg/gnoland/node_inmemory.go +++ b/gno.land/pkg/gnoland/node_inmemory.go @@ -36,7 +36,11 @@ func NewMockedPrivValidator() bft.PrivValidator { } // NewDefaultGenesisConfig creates a default configuration for an in-memory node. -func NewDefaultGenesisConfig(chainid string) *bft.GenesisDoc { +func NewDefaultGenesisConfig(chainid, chaindomain string) *bft.GenesisDoc { + // custom chain domain + var domainParam Param + _ = domainParam.Parse("gno.land/r/sys/params.vm.chain_domain.string=" + chaindomain) + return &bft.GenesisDoc{ GenesisTime: time.Now(), ChainID: chainid, @@ -46,6 +50,9 @@ func NewDefaultGenesisConfig(chainid string) *bft.GenesisDoc { AppState: &GnoGenesisState{ Balances: []Balance{}, Txs: []TxWithMetadata{}, + Params: []Param{ + domainParam, + }, }, } } diff --git a/gno.land/pkg/sdk/vm/gas_test.go b/gno.land/pkg/sdk/vm/gas_test.go index 3a11d97c235..677d86a0331 100644 --- a/gno.land/pkg/sdk/vm/gas_test.go +++ b/gno.land/pkg/sdk/vm/gas_test.go @@ -75,7 +75,7 @@ func TestAddPkgDeliverTx(t *testing.T) { assert.True(t, res.IsOK()) // NOTE: let's try to keep this bellow 100_000 :) - assert.Equal(t, int64(92825), gasDeliver) + assert.Equal(t, int64(93825), gasDeliver) } // Enough gas for a failed transaction. @@ -95,7 +95,7 @@ func TestAddPkgDeliverTxFailed(t *testing.T) { gasDeliver := gctx.GasMeter().GasConsumed() assert.False(t, res.IsOK()) - assert.Equal(t, int64(2231), gasDeliver) + assert.Equal(t, int64(3231), gasDeliver) } // Not enough gas for a failed transaction. diff --git a/gno.land/pkg/sdk/vm/keeper.go b/gno.land/pkg/sdk/vm/keeper.go index 52eff20ea95..e4f7a8543a7 100644 --- a/gno.land/pkg/sdk/vm/keeper.go +++ b/gno.land/pkg/sdk/vm/keeper.go @@ -85,6 +85,7 @@ func NewVMKeeper( bank: bank, prmk: prmk, } + return vmk } @@ -192,6 +193,7 @@ func loadStdlibPackage(pkgPath, stdlibDir string, store gno.Store) { } m := gno.NewMachineWithOptions(gno.MachineOptions{ + // XXX: gno.land, vm.domain, other? PkgPath: "gno.land/r/stdlibs/" + pkgPath, // PkgPath: pkgPath, XXX why? Store: store, @@ -226,20 +228,22 @@ func (vm *VMKeeper) getGnoTransactionStore(ctx sdk.Context) gno.TransactionStore } // Namespace can be either a user or crypto address. -var reNamespace = regexp.MustCompile(`^gno.land/(?:r|p)/([\.~_a-zA-Z0-9]+)`) - -const sysUsersPkgParamPath = "gno.land/r/sys/params.sys.users_pkgpath.string" +var reNamespace = regexp.MustCompile(`^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/(?:r|p)/([\.~_a-zA-Z0-9]+)`) // checkNamespacePermission check if the user as given has correct permssion to on the given pkg path func (vm *VMKeeper) checkNamespacePermission(ctx sdk.Context, creator crypto.Address, pkgPath string) error { - var sysUsersPkg string - vm.prmk.GetString(ctx, sysUsersPkgParamPath, &sysUsersPkg) + sysUsersPkg := vm.getSysUsersPkgParam(ctx) if sysUsersPkg == "" { return nil } + chainDomain := vm.getChainDomainParam(ctx) store := vm.getGnoTransactionStore(ctx) + if !strings.HasPrefix(pkgPath, chainDomain+"/") { + return ErrInvalidPkgPath(pkgPath) // no match + } + match := reNamespace.FindStringSubmatch(pkgPath) switch len(match) { case 0: @@ -248,9 +252,6 @@ func (vm *VMKeeper) checkNamespacePermission(ctx sdk.Context, creator crypto.Add default: panic("invalid pattern while matching pkgpath") } - if len(match) != 2 { - return ErrInvalidPkgPath(pkgPath) - } username := match[1] // if `sysUsersPkg` does not exist -> skip validation. @@ -263,6 +264,7 @@ func (vm *VMKeeper) checkNamespacePermission(ctx sdk.Context, creator crypto.Add pkgAddr := gno.DerivePkgAddr(pkgPath) msgCtx := stdlibs.ExecContext{ ChainID: ctx.ChainID(), + ChainDomain: chainDomain, Height: ctx.BlockHeight(), Timestamp: ctx.BlockTime().Unix(), OrigCaller: creator.Bech32(), @@ -320,6 +322,7 @@ func (vm *VMKeeper) AddPackage(ctx sdk.Context, msg MsgAddPackage) (err error) { memPkg := msg.Package deposit := msg.Deposit gnostore := vm.getGnoTransactionStore(ctx) + chainDomain := vm.getChainDomainParam(ctx) // Validate arguments. if creator.IsZero() { @@ -332,6 +335,9 @@ func (vm *VMKeeper) AddPackage(ctx sdk.Context, msg MsgAddPackage) (err error) { if err := msg.Package.Validate(); err != nil { return ErrInvalidPkgPath(err.Error()) } + if !strings.HasPrefix(pkgPath, chainDomain+"/") { + return ErrInvalidPkgPath("invalid domain: " + pkgPath) + } if pv := gnostore.GetPackage(pkgPath, false); pv != nil { return ErrPkgAlreadyExists("package already exists: " + pkgPath) } @@ -363,6 +369,7 @@ func (vm *VMKeeper) AddPackage(ctx sdk.Context, msg MsgAddPackage) (err error) { // Parse and run the files, construct *PV. msgCtx := stdlibs.ExecContext{ ChainID: ctx.ChainID(), + ChainDomain: chainDomain, Height: ctx.BlockHeight(), Timestamp: ctx.BlockTime().Unix(), OrigCaller: creator.Bech32(), @@ -461,8 +468,10 @@ func (vm *VMKeeper) Call(ctx sdk.Context, msg MsgCall) (res string, err error) { // Make context. // NOTE: if this is too expensive, // could it be safely partially memoized? + chainDomain := vm.getChainDomainParam(ctx) msgCtx := stdlibs.ExecContext{ ChainID: ctx.ChainID(), + ChainDomain: chainDomain, Height: ctx.BlockHeight(), Timestamp: ctx.BlockTime().Unix(), OrigCaller: caller.Bech32(), @@ -531,11 +540,12 @@ func (vm *VMKeeper) Run(ctx sdk.Context, msg MsgRun) (res string, err error) { gnostore := vm.getGnoTransactionStore(ctx) send := msg.Send memPkg := msg.Package + chainDomain := vm.getChainDomainParam(ctx) // coerce path to right one. // the path in the message must be "" or the following path. // this is already checked in MsgRun.ValidateBasic - memPkg.Path = "gno.land/r/" + msg.Caller.String() + "/run" + memPkg.Path = chainDomain + "/r/" + msg.Caller.String() + "/run" // Validate arguments. callerAcc := vm.acck.GetAccount(ctx, caller) @@ -561,6 +571,7 @@ func (vm *VMKeeper) Run(ctx sdk.Context, msg MsgRun) (res string, err error) { // Parse and run the files, construct *PV. msgCtx := stdlibs.ExecContext{ ChainID: ctx.ChainID(), + ChainDomain: chainDomain, Height: ctx.BlockHeight(), Timestamp: ctx.BlockTime().Unix(), OrigCaller: caller.Bech32(), @@ -722,10 +733,12 @@ func (vm *VMKeeper) QueryEval(ctx sdk.Context, pkgPath string, expr string) (res return "", err } // Construct new machine. + chainDomain := vm.getChainDomainParam(ctx) msgCtx := stdlibs.ExecContext{ - ChainID: ctx.ChainID(), - Height: ctx.BlockHeight(), - Timestamp: ctx.BlockTime().Unix(), + ChainID: ctx.ChainID(), + ChainDomain: chainDomain, + Height: ctx.BlockHeight(), + Timestamp: ctx.BlockTime().Unix(), // OrigCaller: caller, // OrigSend: send, // OrigSendSpent: nil, @@ -788,10 +801,12 @@ func (vm *VMKeeper) QueryEvalString(ctx sdk.Context, pkgPath string, expr string return "", err } // Construct new machine. + chainDomain := vm.getChainDomainParam(ctx) msgCtx := stdlibs.ExecContext{ - ChainID: ctx.ChainID(), - Height: ctx.BlockHeight(), - Timestamp: ctx.BlockTime().Unix(), + ChainID: ctx.ChainID(), + ChainDomain: chainDomain, + Height: ctx.BlockHeight(), + Timestamp: ctx.BlockTime().Unix(), // OrigCaller: caller, // OrigSend: jsend, // OrigSendSpent: nil, diff --git a/gno.land/pkg/sdk/vm/keeper_test.go b/gno.land/pkg/sdk/vm/keeper_test.go index 9afbb3de551..f8144988c44 100644 --- a/gno.land/pkg/sdk/vm/keeper_test.go +++ b/gno.land/pkg/sdk/vm/keeper_test.go @@ -22,7 +22,7 @@ import ( "github.com/gnolang/gno/tm2/pkg/store/types" ) -var coinsString = ugnot.ValueString(10000000) +var coinsString = ugnot.ValueString(10_000_000) func TestVMKeeperAddPackage(t *testing.T) { env := setupTestEnv() @@ -68,6 +68,43 @@ func Echo() string { return "hello world" } assert.Equal(t, expected, memFile.Body) } +func TestVMKeeperAddPackage_InvalidDomain(t *testing.T) { + env := setupTestEnv() + ctx := env.vmk.MakeGnoTransactionStore(env.ctx) + + // Give "addr1" some gnots. + addr := crypto.AddressFromPreimage([]byte("addr1")) + acc := env.acck.NewAccountWithAddress(ctx, addr) + env.acck.SetAccount(ctx, acc) + env.bank.SetCoins(ctx, addr, std.MustParseCoins(coinsString)) + assert.True(t, env.bank.GetCoins(ctx, addr).IsEqual(std.MustParseCoins(coinsString))) + + // Create test package. + files := []*gnovm.MemFile{ + { + Name: "test.gno", + Body: `package test +func Echo() string {return "hello world"}`, + }, + } + pkgPath := "anotherdomain.land/r/test" + msg1 := NewMsgAddPackage(addr, pkgPath, files) + assert.Nil(t, env.vmk.getGnoTransactionStore(ctx).GetPackage(pkgPath, false)) + + err := env.vmk.AddPackage(ctx, msg1) + + assert.Error(t, err, ErrInvalidPkgPath("invalid domain: anotherdomain.land/r/test")) + assert.Nil(t, env.vmk.getGnoTransactionStore(ctx).GetPackage(pkgPath, false)) + + err = env.vmk.AddPackage(ctx, msg1) + assert.Error(t, err, ErrInvalidPkgPath("invalid domain: anotherdomain.land/r/test")) + + // added package is formatted + store := env.vmk.getGnoTransactionStore(ctx) + memFile := store.GetMemFile("gno.land/r/test", "test.gno") + assert.Nil(t, memFile) +} + // Sending total send amount succeeds. func TestVMKeeperOrigSend1(t *testing.T) { env := setupTestEnv() diff --git a/gno.land/pkg/sdk/vm/msgs.go b/gno.land/pkg/sdk/vm/msgs.go index d5b82067a98..1ce648acb19 100644 --- a/gno.land/pkg/sdk/vm/msgs.go +++ b/gno.land/pkg/sdk/vm/msgs.go @@ -186,8 +186,8 @@ func (msg MsgRun) ValidateBasic() error { } // Force memPkg path to the reserved run path. - wantPath := "gno.land/r/" + msg.Caller.String() + "/run" - if path := msg.Package.Path; path != "" && path != wantPath { + wantSuffix := "/r/" + msg.Caller.String() + "/run" + if path := msg.Package.Path; path != "" && !strings.HasSuffix(path, wantSuffix) { return ErrInvalidPkgPath(fmt.Sprintf("invalid pkgpath for MsgRun: %q", path)) } diff --git a/gno.land/pkg/sdk/vm/params.go b/gno.land/pkg/sdk/vm/params.go new file mode 100644 index 00000000000..248fb8a81fb --- /dev/null +++ b/gno.land/pkg/sdk/vm/params.go @@ -0,0 +1,20 @@ +package vm + +import "github.com/gnolang/gno/tm2/pkg/sdk" + +const ( + sysUsersPkgParamPath = "gno.land/r/sys/params.sys.users_pkgpath.string" + chainDomainParamPath = "gno.land/r/sys/params.chain_domain.string" +) + +func (vm *VMKeeper) getChainDomainParam(ctx sdk.Context) string { + chainDomain := "gno.land" // default + vm.prmk.GetString(ctx, chainDomainParamPath, &chainDomain) + return chainDomain +} + +func (vm *VMKeeper) getSysUsersPkgParam(ctx sdk.Context) string { + var sysUsersPkg string + vm.prmk.GetString(ctx, sysUsersPkgParamPath, &sysUsersPkg) + return sysUsersPkg +} diff --git a/gnovm/cmd/gno/test.go b/gnovm/cmd/gno/test.go index 04a3808718d..511a704dd7d 100644 --- a/gnovm/cmd/gno/test.go +++ b/gnovm/cmd/gno/test.go @@ -205,7 +205,7 @@ func execTest(cfg *testCfg, args []string, io commands.IO) error { if gnoPkgPath == "" { // unable to read pkgPath from gno.mod, generate a random realm path io.ErrPrintfln("--- WARNING: unable to read package path from gno.mod or gno root directory; try creating a gno.mod file") - gnoPkgPath = gno.RealmPathPrefix + strings.ToLower(random.RandStr(8)) + gnoPkgPath = "gno.land/r/" + strings.ToLower(random.RandStr(8)) // XXX: gno.land hardcoded for convenience. } } diff --git a/gnovm/memfile.go b/gnovm/memfile.go index a37bba6ef3d..6988c893dd7 100644 --- a/gnovm/memfile.go +++ b/gnovm/memfile.go @@ -41,7 +41,7 @@ const pathLengthLimit = 256 var ( rePkgName = regexp.MustCompile(`^[a-z][a-z0-9_]*$`) - rePkgOrRlmPath = regexp.MustCompile(`^gno\.land\/(?:p|r)(?:\/_?[a-z]+[a-z0-9_]*)+$`) + rePkgOrRlmPath = regexp.MustCompile(`^([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.[a-zA-Z]{2,}\/(?:p|r)(?:\/_?[a-z]+[a-z0-9_]*)+$`) reFileName = regexp.MustCompile(`^([a-zA-Z0-9_]*\.[a-z0-9_\.]*|LICENSE|README)$`) ) diff --git a/gnovm/memfile_test.go b/gnovm/memfile_test.go index c93c251b0e7..5ef70e9e868 100644 --- a/gnovm/memfile_test.go +++ b/gnovm/memfile_test.go @@ -158,13 +158,13 @@ func TestMemPackage_Validate(t *testing.T) { "invalid package/realm path", }, { - "Invalid path", + "Custom domain", &MemPackage{ Name: "hey", Path: "github.com/p/path/path", Files: []*MemFile{{Name: "a.gno"}}, }, - "invalid package/realm path", + "", }, { "Special character", diff --git a/gnovm/pkg/gnolang/helpers.go b/gnovm/pkg/gnolang/helpers.go index d3a8485ee17..ddc1fd2fa55 100644 --- a/gnovm/pkg/gnolang/helpers.go +++ b/gnovm/pkg/gnolang/helpers.go @@ -10,22 +10,21 @@ import ( // ---------------------------------------- // Functions centralizing definitions -// RealmPathPrefix is the prefix used to identify pkgpaths which are meant to -// be realms and as such to have their state persisted. This is used by [IsRealmPath]. -const ( - RealmPathPrefix = "gno.land/r/" - PackagePathPrefix = "gno.land/p/" +// ReRealmPath and RePackagePath 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_/]+`) ) // ReGnoRunPath is the path used for realms executed in maketx run. -// These are not considered realms, as an exception to the RealmPathPrefix rule. -var ReGnoRunPath = regexp.MustCompile(`^gno\.land/r/g[a-z0-9]+/run$`) +// These are not considered realms, as an exception to the ReRealmPathPrefix rule. +var ReGnoRunPath = regexp.MustCompile(`^([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.[a-zA-Z]{2,}/r/g[a-z0-9]+/run$`) // IsRealmPath determines whether the given pkgpath is for a realm, and as such // should persist the global state. func IsRealmPath(pkgPath string) bool { - return strings.HasPrefix(pkgPath, RealmPathPrefix) && - // MsgRun pkgPath aren't realms + return ReRealmPath.MatchString(pkgPath) && !ReGnoRunPath.MatchString(pkgPath) } @@ -33,16 +32,17 @@ func IsRealmPath(pkgPath string) bool { // It only considers "pure" those starting with gno.land/p/, so it returns false for // stdlib packages and MsgRun paths. func IsPurePackagePath(pkgPath string) bool { - return strings.HasPrefix(pkgPath, PackagePathPrefix) + return RePackagePath.MatchString(pkgPath) } // IsStdlib determines whether s is a pkgpath for a standard library. func IsStdlib(s string) bool { - // NOTE(morgan): this is likely to change in the future as we add support for - // IBC/ICS and we allow import paths to other chains. It might be good to - // (eventually) follow the same rule as Go, which is: does the first - // element of the import path contain a dot? - return !strings.HasPrefix(s, "gno.land/") + idx := strings.IndexByte(s, '/') + if idx < 0 { + // If no '/' is found, consider the whole string + return strings.IndexByte(s, '.') < 0 + } + return strings.IndexByte(s[:idx+1], '.') < 0 } // ---------------------------------------- diff --git a/gnovm/pkg/test/test.go b/gnovm/pkg/test/test.go index 5de37a68405..3ea3d4bc9bd 100644 --- a/gnovm/pkg/test/test.go +++ b/gnovm/pkg/test/test.go @@ -53,6 +53,7 @@ func Context(pkgPath string, send std.Coins) *teststd.TestExecContext { } ctx := stdlibs.ExecContext{ ChainID: "dev", + ChainDomain: "tests.gno.land", Height: DefaultHeight, Timestamp: DefaultTimestamp, OrigCaller: DefaultCaller, diff --git a/gnovm/stdlibs/generated.go b/gnovm/stdlibs/generated.go index a2d82b0bc60..67b492a34b2 100644 --- a/gnovm/stdlibs/generated.go +++ b/gnovm/stdlibs/generated.go @@ -468,6 +468,26 @@ var nativeFuncs = [...]NativeFunc{ )) }, }, + { + "std", + "GetChainDomain", + []gno.FieldTypeExpr{}, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("string")}, + }, + true, + func(m *gno.Machine) { + r0 := libs_std.GetChainDomain( + m, + ) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, { "std", "GetHeight", diff --git a/gnovm/stdlibs/std/context.go b/gnovm/stdlibs/std/context.go index 01e763ab82e..a8ef500c346 100644 --- a/gnovm/stdlibs/std/context.go +++ b/gnovm/stdlibs/std/context.go @@ -9,6 +9,7 @@ import ( type ExecContext struct { ChainID string + ChainDomain string Height int64 Timestamp int64 // seconds TimestampNano int64 // nanoseconds, only used for testing. diff --git a/gnovm/stdlibs/std/native.gno b/gnovm/stdlibs/std/native.gno index 5421e231de2..0dcde1148e1 100644 --- a/gnovm/stdlibs/std/native.gno +++ b/gnovm/stdlibs/std/native.gno @@ -10,8 +10,9 @@ func AssertOriginCall() // injected // MsgRun. func IsOriginCall() bool // injected -func GetChainID() string // injected -func GetHeight() int64 // injected +func GetChainID() string // injected +func GetChainDomain() string // injected +func GetHeight() int64 // injected func GetOrigSend() Coins { den, amt := origSend() diff --git a/gnovm/stdlibs/std/native.go b/gnovm/stdlibs/std/native.go index 3fe5fbb9889..fb181d9be31 100644 --- a/gnovm/stdlibs/std/native.go +++ b/gnovm/stdlibs/std/native.go @@ -27,6 +27,10 @@ func GetChainID(m *gno.Machine) string { return GetContext(m).ChainID } +func GetChainDomain(m *gno.Machine) string { + return GetContext(m).ChainDomain +} + func GetHeight(m *gno.Machine) int64 { return GetContext(m).Height } diff --git a/gnovm/tests/files/std5.gno b/gnovm/tests/files/std5.gno index 54cfb7846ab..2baba6b5005 100644 --- a/gnovm/tests/files/std5.gno +++ b/gnovm/tests/files/std5.gno @@ -13,10 +13,10 @@ func main() { // Stacktrace: // panic: frame not found -// callerAt(n) +// callerAt(n) // gonative:std.callerAt // std.GetCallerAt(2) -// std/native.gno:44 +// std/native.gno:45 // main() // main/files/std5.gno:10 diff --git a/gnovm/tests/files/std8.gno b/gnovm/tests/files/std8.gno index 27545f267ce..4f749c3a6e1 100644 --- a/gnovm/tests/files/std8.gno +++ b/gnovm/tests/files/std8.gno @@ -23,10 +23,10 @@ func main() { // Stacktrace: // panic: frame not found -// callerAt(n) +// callerAt(n) // gonative:std.callerAt // std.GetCallerAt(4) -// std/native.gno:44 +// std/native.gno:45 // fn() // main/files/std8.gno:16 // testutils.WrapCall(inner) diff --git a/gnovm/tests/files/zrealm_natbind1_stdlibs.gno b/gnovm/tests/files/zrealm_natbind1_stdlibs.gno new file mode 100644 index 00000000000..f44b6ab4fcf --- /dev/null +++ b/gnovm/tests/files/zrealm_natbind1_stdlibs.gno @@ -0,0 +1,16 @@ +// PKGPATH: gno.land/r/test +package test + +import ( + "std" +) + +func main() { + println(std.GetChainDomain()) +} + +// Output: +// tests.gno.land + +// Realm: +// switchrealm["gno.land/r/test"]