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: support custom VM domain #2911

Draft
wants to merge 23 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions contribs/gnodev/pkg/dev/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,7 @@ 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)
Expand All @@ -564,10 +564,12 @@ func newNodeConfig(tmc *tmcfg.Config, chainid string, appstate gnoland.GnoGenesi
},
}

return &gnoland.InMemoryNodeConfig{
cfg := &gnoland.InMemoryNodeConfig{
PrivValidator: pv,
TMConfig: tmc,
Genesis: genesis,
GenesisMaxVMCycles: 100_000_000,
}
cfg.InitChainerConfig.ChainDomain = chaindomain
return cfg
}
16 changes: 8 additions & 8 deletions gno.land/cmd/gnoland/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ type startCfg struct {
genesisRemote string // TODO: remove as part of https://github.com/gnolang/gno/issues/1952
genesisFile string
chainID string
chainDomain string
dataDir string
genesisMaxVMCycles int64
config string
lazyInit bool

Expand Down Expand Up @@ -116,6 +116,13 @@ func (c *startCfg) RegisterFlags(fs *flag.FlagSet) {
"the ID of the chain",
)

fs.StringVar(
&c.chainDomain,
"chaindomain",
"gno.land",
"the domain of the chain for packages",
)

fs.StringVar(
&c.gnoRootDir,
"gnoroot-dir",
Expand All @@ -137,13 +144,6 @@ func (c *startCfg) RegisterFlags(fs *flag.FlagSet) {
"replacement for '%%REMOTE%%' in genesis",
)

fs.Int64Var(
moul marked this conversation as resolved.
Show resolved Hide resolved
&c.genesisMaxVMCycles,
"genesis-max-vm-cycles",
100_000_000,
"set maximum allowed vm cycles per operation. Zero means no limit.",
)

fs.StringVar(
&c.config,
flagConfigFlag,
Expand Down
6 changes: 5 additions & 1 deletion gno.land/pkg/gnoland/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ func TestAppOptions(db dbm.DB) *AppOptions {
GenesisTxResultHandler: PanicOnFailingTxResultHandler,
StdlibDir: filepath.Join(gnoenv.RootDir(), "gnovm", "stdlibs"),
CacheStdlibLoad: true,
ChainDomain: "gno.land",
},
}
}
Expand Down Expand Up @@ -88,7 +89,7 @@ func NewAppWithOptions(cfg *AppOptions) (abci.Application, error) {
// Construct keepers.
acctKpr := auth.NewAccountKeeper(mainKey, ProtoGnoAccount)
bankKpr := bank.NewBankKeeper(acctKpr)
vmk := vm.NewVMKeeper(baseKey, mainKey, acctKpr, bankKpr, cfg.MaxCycles)
vmk := vm.NewVMKeeper(baseKey, mainKey, acctKpr, bankKpr, cfg.ChainDomain, cfg.MaxCycles)

// Set InitChainer
icc := cfg.InitChainerConfig
Expand Down Expand Up @@ -223,6 +224,9 @@ type InitChainerConfig struct {
// called several times.
CacheStdlibLoad bool

// ChainDomain is the primary domain name for the chain and its packages.
ChainDomain string
Comment on lines +226 to +227
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why add this here instead of in genesis?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that I prefer your idea. I'm waiting for more feedback before making the change.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I agree it should be in genesis. Allows us more easily to find problems when there's a mismatch between two nodes.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

switching the PR to draft until I finish and merge #3003.


// These fields are passed directly by NewAppWithOptions, and should not be
// configurable by end-users.
baseApp *sdk.BaseApp
Expand Down
1 change: 1 addition & 0 deletions gno.land/pkg/integration/testing_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ func TestingMinimalNodeConfig(t TestingTS, gnoroot string) *gnoland.InMemoryNode
InitChainerConfig: gnoland.InitChainerConfig{
GenesisTxResultHandler: gnoland.PanicOnFailingTxResultHandler,
CacheStdlibLoad: true,
ChainDomain: "gno.land",
},
}
}
Expand Down
3 changes: 2 additions & 1 deletion gno.land/pkg/sdk/vm/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ func _setupTestEnv(cacheStdlibs bool) testEnv {
ctx := sdk.NewContext(sdk.RunTxModeDeliver, ms, &bft.Header{ChainID: "test-chain-id"}, log.NewNoopLogger())
acck := authm.NewAccountKeeper(iavlCapKey, std.ProtoBaseAccount)
bank := bankm.NewBankKeeper(acck)
vmk := NewVMKeeper(baseCapKey, iavlCapKey, acck, bank, 100_000_000)
chainDomain := "gno.land"
vmk := NewVMKeeper(baseCapKey, iavlCapKey, acck, bank, chainDomain, 100_000_000)

mcw := ms.MultiCacheWrap()
vmk.Initialize(log.NewNoopLogger(), mcw)
Expand Down
26 changes: 19 additions & 7 deletions gno.land/pkg/sdk/vm/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,11 @@
// cached, the DeliverTx persistent state.
gnoStore gno.Store

maxCycles int64 // max allowed cylces on VM executions
domain string // chain domain
maxCycles int64 // max allowed cylces on VM executions

// internal
reNamespace *regexp.Regexp
}

// NewVMKeeper returns a new VMKeeper.
Expand All @@ -72,6 +76,7 @@
iavlKey store.StoreKey,
acck auth.AccountKeeper,
bank bank.BankKeeper,
chainDomain string,
maxCycles int64,
) *VMKeeper {
// TODO: create an Options struct to avoid too many constructor parameters
Expand All @@ -80,8 +85,12 @@
iavlKey: iavlKey,
acck: acck,
bank: bank,
domain: chainDomain,
maxCycles: maxCycles,
}

// Namespace can be either a user or crypto address.
vmk.reNamespace = regexp.MustCompile(`^` + chainDomain + `/(?:r|p)/([\.~_a-zA-Z0-9]+)`)
moul marked this conversation as resolved.
Show resolved Hide resolved
return vmk
}

Expand Down Expand Up @@ -189,6 +198,7 @@
}

m := gno.NewMachineWithOptions(gno.MachineOptions{
// XXX: gno.land, vm.domain, other?
PkgPath: "gno.land/r/stdlibs/" + pkgPath,
// PkgPath: pkgPath, XXX why?
Output: os.Stdout,
Expand Down Expand Up @@ -223,16 +233,13 @@
return txStore
}

// Namespace can be either a user or crypto address.
var reNamespace = regexp.MustCompile(`^gno.land/(?: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 {
const sysUsersPkg = "gno.land/r/sys/users"
sysUsersPkg := vm.domain + "/r/sys/users" // configurable through sys/params

store := vm.getGnoTransactionStore(ctx)

match := reNamespace.FindStringSubmatch(pkgPath)
match := vm.reNamespace.FindStringSubmatch(pkgPath)
switch len(match) {
case 0:
return ErrInvalidPkgPath(pkgPath) // no match
Expand All @@ -255,6 +262,7 @@
pkgAddr := gno.DerivePkgAddr(pkgPath)
msgCtx := stdlibs.ExecContext{
ChainID: ctx.ChainID(),
ChainDomain: vm.domain,

Check warning on line 265 in gno.land/pkg/sdk/vm/keeper.go

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/sdk/vm/keeper.go#L265

Added line #L265 was not covered by tests
Height: ctx.BlockHeight(),
Timestamp: ctx.BlockTime().Unix(),
OrigCaller: creator.Bech32(),
Expand Down Expand Up @@ -324,6 +332,9 @@
if err := msg.Package.Validate(); err != nil {
return ErrInvalidPkgPath(err.Error())
}
if !strings.HasPrefix(pkgPath, vm.domain+"/") {
return ErrInvalidPkgPath("invalid domain: " + pkgPath)

Check warning on line 336 in gno.land/pkg/sdk/vm/keeper.go

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/sdk/vm/keeper.go#L336

Added line #L336 was not covered by tests
}
if pv := gnostore.GetPackage(pkgPath, false); pv != nil {
return ErrPkgAlreadyExists("package already exists: " + pkgPath)
}
Expand Down Expand Up @@ -529,7 +540,7 @@
// 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 = vm.domain + "/r/" + msg.Caller.String() + "/run"

// Validate arguments.
callerAcc := vm.acck.GetAccount(ctx, caller)
Expand All @@ -555,6 +566,7 @@
// Parse and run the files, construct *PV.
msgCtx := stdlibs.ExecContext{
ChainID: ctx.ChainID(),
ChainDomain: vm.domain,
Height: ctx.BlockHeight(),
Timestamp: ctx.BlockTime().Unix(),
Msg: msg,
Expand Down
4 changes: 2 additions & 2 deletions gno.land/pkg/sdk/vm/msgs.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,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))
}

Expand Down
2 changes: 1 addition & 1 deletion gnovm/cmd/gno/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ func gnoTestPkg(
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 + random.RandStr(8)
gnoPkgPath = "gno.land/r/" + random.RandStr(8) // XXX: "gno.land" hardcoded for convenience
}
}
memPkg := gno.ReadMemPackage(pkgPath, gnoPkgPath)
Expand Down
24 changes: 13 additions & 11 deletions gnovm/pkg/gnolang/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,29 +10,31 @@ import (
// ----------------------------------------
// Functions centralizing definitions

// RealmPathPrefix is the prefix used to identify pkgpaths which are meant to
// ReGnoRealmPath is the regex 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/"
var ReGnoRealmPath = regexp.MustCompile(`^([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.[a-zA-Z]{2,}/r/`)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should use this to validate the chain domain elsewhere.


// 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 ReGnoRealmPath 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 ReGnoRealmPath.MatchString(pkgPath) &&
!ReGnoRunPath.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/")
parts := strings.Split(s, "/")
if len(parts) > 0 {
// Check if the first part contains a dot
if strings.Contains(parts[0], ".") {
return false // It's a domain, so it's not part of the standard library
}
}
Comment on lines +30 to +36
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return strings.IndexByte(s[:strings.IndexByte(s, '/')+1], '.') < 0

return true
}

// ----------------------------------------
Expand Down
20 changes: 20 additions & 0 deletions gnovm/stdlibs/generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions gnovm/stdlibs/std/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

type ExecContext struct {
ChainID string
ChainDomain string
Height int64
Timestamp int64 // seconds
TimestampNano int64 // nanoseconds, only used for testing.
Expand Down
5 changes: 3 additions & 2 deletions gnovm/stdlibs/std/native.gno
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
4 changes: 4 additions & 0 deletions gnovm/stdlibs/std/native.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ func GetChainID(m *gno.Machine) string {
return GetContext(m).ChainID
}

func GetChainDomain(m *gno.Machine) string {
return GetContext(m).ChainDomain
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is used to write the import paths, should this not be largely known by gno files already (so no need for the function)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's for when your p/ package is imported from one chain to another. The code remains the same, but the behavior may change.

}

func GetHeight(m *gno.Machine) int64 {
return GetContext(m).Height
}
Expand Down
6 changes: 6 additions & 0 deletions misc/genstd/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
run:
cd ../../gnovm/stdlibs && go run ../../misc/genstd
cd ../../gnovm/tests/stdlibs && go run ../../../misc/genstd

test:
go test -v .
2 changes: 1 addition & 1 deletion tm2/pkg/std/memfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)$`)
)

Expand Down
6 changes: 3 additions & 3 deletions tm2/pkg/telemetry/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ type Config struct {
func DefaultTelemetryConfig() *Config {
return &Config{
MetricsEnabled: false,
MeterName: "gno.land",
ServiceName: "gno.land",
ServiceInstanceID: "gno-node-1",
MeterName: "tm2",
ServiceName: "tm2",
ServiceInstanceID: "tm2-node-1",
ExporterEndpoint: "",
}
}
Expand Down
Loading