diff --git a/contribs/gnodev/README.md b/contribs/gnodev/README.md index e0a349c257a..ee1dc433ebc 100644 --- a/contribs/gnodev/README.md +++ b/contribs/gnodev/README.md @@ -111,6 +111,8 @@ FLAGS -chain-domain gno.land set node ChainDomain -chain-id dev set node ChainID -deploy-key g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5 default key name or Bech32 address for deploying packages + -empty-blocks=false enable creation of empty blocks (default: ~1s interval) + -empty-blocks-interval 1s set the interval for creating empty blocks -genesis ... load the given genesis file -interactive=false enable gnodev interactive mode -lazy-loader=true enable lazy loader @@ -154,6 +156,8 @@ FLAGS -chain-domain gno.land set node ChainDomain -chain-id dev set node ChainID -deploy-key g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5 default key name or Bech32 address for deploying packages + -empty-blocks=false enable creation of empty blocks (default: ~1s interval) + -empty-blocks-interval 0s set the interval for creating empty blocks -genesis ... load the given genesis file -interactive=false enable gnodev interactive mode -lazy-loader=false enable lazy loader diff --git a/contribs/gnodev/app_config.go b/contribs/gnodev/app_config.go index 7f2261414a4..58a9c4412d6 100644 --- a/contribs/gnodev/app_config.go +++ b/contribs/gnodev/app_config.go @@ -1,6 +1,9 @@ package main -import "flag" +import ( + "flag" + "time" +) type AppConfig struct { // Listeners @@ -31,17 +34,19 @@ type AppConfig struct { resolvers varResolver // Node Configuration - logFormat string - lazyLoader bool - verbose bool - noWatch bool - noReplay bool - maxGas int64 - chainId string - chainDomain string - unsafeAPI bool - interactive bool - paths string + logFormat string + lazyLoader bool + verbose bool + noWatch bool + noReplay bool + maxGas int64 + chainId string + chainDomain string + unsafeAPI bool + interactive bool + paths string + emptyBlocks bool + emptyBlocksInterval time.Duration } func (c *AppConfig) RegisterFlagsWith(fs *flag.FlagSet, defaultCfg AppConfig) { @@ -220,6 +225,20 @@ func (c *AppConfig) RegisterFlagsWith(fs *flag.FlagSet, defaultCfg AppConfig) { `additional paths to preload in the form of "gno.land/r/my/realm", separated by commas; glob is supported`, ) + fs.BoolVar( + &c.emptyBlocks, + "empty-blocks", + defaultCfg.emptyBlocks, + "enable creation of empty blocks (default: ~1s interval)", + ) + + fs.DurationVar( + &c.emptyBlocksInterval, + "empty-blocks-interval", + defaultCfg.emptyBlocksInterval, + "set the interval for creating empty blocks", + ) + fs.BoolVar( &c.verbose, "v", diff --git a/contribs/gnodev/command_local.go b/contribs/gnodev/command_local.go index de40d111408..663f58d5f47 100644 --- a/contribs/gnodev/command_local.go +++ b/contribs/gnodev/command_local.go @@ -8,6 +8,7 @@ import ( "os" "path/filepath" "strings" + "time" "github.com/gnolang/gno/contribs/gnodev/pkg/packages" "github.com/gnolang/gno/gnovm/pkg/gnoenv" @@ -38,6 +39,8 @@ var defaultLocalAppConfig = AppConfig{ interactive: isatty.IsTerminal(os.Stdout.Fd()), unsafeAPI: true, lazyLoader: true, + emptyBlocks: false, + emptyBlocksInterval: time.Second * 1, // As we have no reason to configure this yet, set this to random port // to avoid potential conflict with other app diff --git a/contribs/gnodev/setup_node.go b/contribs/gnodev/setup_node.go index 43313575be8..85eacdf3421 100644 --- a/contribs/gnodev/setup_node.go +++ b/contribs/gnodev/setup_node.go @@ -111,6 +111,8 @@ func setupDevNodeConfig( config.NoReplay = cfg.noReplay config.MaxGasPerBlock = cfg.maxGas config.ChainID = cfg.chainId + config.TMConfig.Consensus.CreateEmptyBlocks = cfg.emptyBlocks + config.TMConfig.Consensus.CreateEmptyBlocksInterval = cfg.emptyBlocksInterval // other listeners config.TMConfig.P2P.ListenAddress = defaultLocalAppConfig.nodeP2PListenerAddr diff --git a/gno.land/pkg/gnoland/mock_test.go b/gno.land/pkg/gnoland/mock_test.go index acf42e9921c..3aa01006cb8 100644 --- a/gno.land/pkg/gnoland/mock_test.go +++ b/gno.land/pkg/gnoland/mock_test.go @@ -171,7 +171,7 @@ func (m *mockParamsKeeper) GetString(ctx sdk.Context, key string, ptr *string) func (m *mockParamsKeeper) GetInt64(ctx sdk.Context, key string, ptr *int64) {} func (m *mockParamsKeeper) GetUint64(ctx sdk.Context, key string, ptr *uint64) {} func (m *mockParamsKeeper) GetBool(ctx sdk.Context, key string, ptr *bool) {} -func (m *mockParamsKeeper) GetBytes(ctx sdk.Context, key string, ptr *[]byte) {} +func (m *mockParamsKeeper) GetBytes(ctx sdk.Context, key string) []byte { return nil } func (m *mockParamsKeeper) GetStrings(ctx sdk.Context, key string, ptr *[]string) {} func (m *mockParamsKeeper) SetString(ctx sdk.Context, key string, value string) {} @@ -181,10 +181,7 @@ func (m *mockParamsKeeper) SetBool(ctx sdk.Context, key string, value bool) func (m *mockParamsKeeper) SetBytes(ctx sdk.Context, key string, value []byte) {} func (m *mockParamsKeeper) SetStrings(ctx sdk.Context, key string, value []string) {} -func (m *mockParamsKeeper) Has(ctx sdk.Context, key string) bool { return false } -func (m *mockParamsKeeper) GetRaw(ctx sdk.Context, key string) []byte { return nil } -func (m *mockParamsKeeper) SetRaw(ctx sdk.Context, key string, value []byte) {} - +func (m *mockParamsKeeper) Has(ctx sdk.Context, key string) bool { return false } func (m *mockParamsKeeper) GetStruct(ctx sdk.Context, key string, strctPtr any) {} func (m *mockParamsKeeper) SetStruct(ctx sdk.Context, key string, strct any) {} diff --git a/gno.land/pkg/integration/testdata/maketx_call_send.txtar b/gno.land/pkg/integration/testdata/maketx_call_send.txtar new file mode 100644 index 00000000000..a9a50296c59 --- /dev/null +++ b/gno.land/pkg/integration/testdata/maketx_call_send.txtar @@ -0,0 +1,36 @@ +# load the package +loadpkg gno.land/r/foo/call_realm $WORK/realm + +# start a new node +gnoland start + +## user balance before realm send +gnokey query auth/accounts/$test1_user_addr +stdout '"coins": "9999998810000ugnot"' + +## realm balance before realm send +gnokey query auth/accounts/g1x4ykzcqksj2hc5qpvr8kd9zaffkd82rvmzqup7 +stdout '"coins": ""' + +# call to realm with -send +gnokey maketx call -send 42ugnot -pkgpath gno.land/r/foo/call_realm -func GimmeMoney -gas-fee 1000000ugnot -gas-wanted 3000000 -broadcast -chainid=tendermint_test test1 +stdout '("send: 42ugnot")' + +## user balance after realm send +# reduced by -gas-fee AND -send +gnokey query auth/accounts/$test1_user_addr +stdout '"coins": "9999997809958ugnot"' + +## realm balance after realm send +gnokey query auth/accounts/g1x4ykzcqksj2hc5qpvr8kd9zaffkd82rvmzqup7 +stdout '"coins": "42ugnot"' + + +-- realm/realm.gno -- +package call_realm + +import "chain/banker" + +func GimmeMoney(cur realm) string { + return "send: " + banker.OriginSend().String() +} diff --git a/gno.land/pkg/integration/testdata/run.txtar b/gno.land/pkg/integration/testdata/maketx_run.txtar similarity index 100% rename from gno.land/pkg/integration/testdata/run.txtar rename to gno.land/pkg/integration/testdata/maketx_run.txtar diff --git a/gno.land/pkg/integration/testdata/maketx_run_send.txtar b/gno.land/pkg/integration/testdata/maketx_run_send.txtar new file mode 100644 index 00000000000..e601536439f --- /dev/null +++ b/gno.land/pkg/integration/testdata/maketx_run_send.txtar @@ -0,0 +1,76 @@ +## start a new node +gnoland start + +## user balance before realm send +gnokey query auth/accounts/$test1_user_addr +stdout '"coins": "10000000000000ugnot"' + +## run script/print_originsend.gno with send flag +gnokey maketx run -send 42ugnot -gas-fee 1000000ugnot -gas-wanted 20000000 -broadcast -chainid=tendermint_test test1 $WORK/script/print_originsend.gno +stdout 'send: 42ugnot' + +## user balance after realm send +# only reduced by -gas-fee, -send does not affect balance since no transfer +# occurred and the run script shares the same address as the user. +gnokey query auth/accounts/$test1_user_addr +stdout '"coins": "9999999000000ugnot"' + +## run script/newbanker_realmissue.gno with send flag +# must fail because this banker type is not allowed in ephemerals. +! gnokey maketx run -send 42ugnot -gas-fee 1000000ugnot -gas-wanted 20000000 -broadcast -chainid=tendermint_test test1 $WORK/script/newbanker_realmissue.gno +stderr 'invalid banker type 3 for ephemeral' + +## run script/newbanker_realmsend.gno with send flag +# must fail because this banker type is not allowed in ephemerals. +! gnokey maketx run -send 42ugnot -gas-fee 1000000ugnot -gas-wanted 20000000 -broadcast -chainid=tendermint_test test1 $WORK/script/newbanker_realmsend.gno +stderr 'invalid banker type 2 for ephemeral' + +## run script/newbanker_originsend.gno with send flag +gnokey maketx run -send 42ugnot -gas-fee 1000000ugnot -gas-wanted 20000000 -broadcast -chainid=tendermint_test test1 $WORK/script/newbanker_originsend.gno +stdout 'OK!' + +## user balance after coin transfer in newbanker_originsend +# reduced by -gas-fee and -send +gnokey query auth/accounts/$test1_user_addr +stdout '"coins": "9999997999958ugnot"' + +-- script/print_originsend.gno -- +package main + +import "chain/banker" + +func main() { + println("send:", banker.OriginSend()) +} +-- script/newbanker_realmissue.gno -- +package main + +import "chain/banker" + +func main() { + banker.NewBanker(banker.BankerTypeRealmIssue) +} +-- script/newbanker_realmsend.gno -- +package main + +import "chain/banker" + +func main() { + banker.NewBanker(banker.BankerTypeRealmSend) +} +-- script/newbanker_originsend.gno -- +package main + +import ( + "chain" + "chain/banker" + "chain/runtime" +) + +func main() { + to := chain.PackageAddress("gno.land/r/gimmemoney") + banker.NewBanker(banker.BankerTypeOriginSend).SendCoins( + runtime.CurrentRealm().Address(), to, + chain.Coins{{"ugnot", 42}}, + ) +} diff --git a/gno.land/pkg/keyscli/run.go b/gno.land/pkg/keyscli/run.go index 913df5a450f..aa8c2e6d10e 100644 --- a/gno.land/pkg/keyscli/run.go +++ b/gno.land/pkg/keyscli/run.go @@ -20,6 +20,7 @@ import ( type MakeRunCfg struct { RootCfg *client.MakeTxCfg + Send string MaxDeposit string } @@ -42,6 +43,12 @@ func NewMakeRunCmd(rootCfg *client.MakeTxCfg, cmdio commands.IO) *commands.Comma } func (c *MakeRunCfg) RegisterFlags(fs *flag.FlagSet) { + fs.StringVar( + &c.Send, + "send", + "", + "send amount", + ) fs.StringVar( &c.MaxDeposit, "max-deposit", @@ -75,7 +82,13 @@ func execMakeRun(cfg *MakeRunCfg, args []string, cmdio commands.IO) error { } caller := info.GetAddress() - // Parase deposit amount + // Parse send amount. + send, err := std.ParseCoins(cfg.Send) + if err != nil { + return errors.Wrap(err, "parsing send coins") + } + + // Parse deposit amount deposit, err := std.ParseCoins(cfg.MaxDeposit) if err != nil { return errors.Wrap(err, "parsing storage deposit coins") @@ -133,6 +146,7 @@ func execMakeRun(cfg *MakeRunCfg, args []string, cmdio commands.IO) error { msg := vm.MsgRun{ Caller: caller, Package: memPkg, + Send: send, MaxDeposit: deposit, } tx := std.Tx{ diff --git a/gnovm/stdlibs/chain/banker/banker.gno b/gnovm/stdlibs/chain/banker/banker.gno index 6f6effad804..afb012ab288 100644 --- a/gnovm/stdlibs/chain/banker/banker.gno +++ b/gnovm/stdlibs/chain/banker/banker.gno @@ -68,7 +68,7 @@ func (b BankerType) String() string { // NewBanker returns a new Banker, with its capabilities matching the given // [BankerType]. func NewBanker(bt BankerType) Banker { - assertCallerIsRealm() + assertCallerIsRealmOrEphemeral(uint8(bt)) if bt >= maxBanker { panic("invalid banker type") } @@ -179,7 +179,7 @@ func OriginSend() chain.Coins { return coins } -func assertCallerIsRealm() +func assertCallerIsRealmOrEphemeral(bt uint8) func originSend() (denoms []string, amounts []int64) // expandNative expands coins for usage within natively bound functions. diff --git a/gnovm/stdlibs/chain/banker/banker.go b/gnovm/stdlibs/chain/banker/banker.go index e6a34a57601..f7f8d8cde85 100644 --- a/gnovm/stdlibs/chain/banker/banker.go +++ b/gnovm/stdlibs/chain/banker/banker.go @@ -96,11 +96,30 @@ func CompactCoins(denoms []string, amounts []int64) std.Coins { return coins } -func X_assertCallerIsRealm(m *gno.Machine) { +func X_assertCallerIsRealmOrEphemeral(m *gno.Machine, bt uint8) { frame := m.Frames[m.NumFrames()-2] - if path := frame.LastPackage.PkgPath; !gno.IsRealmPath(path) { - m.PanicString("caller is not a realm") + path := frame.LastPackage.PkgPath + if gno.IsRealmPath(path) { + return } + if gno.IsEphemeralPath(path) { + switch bt { + case btOriginSend, btReadonly: + return + default: + // Other bankType than OriginSend and ReadOnly are forbidden for an + // ephemeral. + // The reason is that a malicious script could easily drain users' funds + // if they didn't carefully check the code. Restricting the banker type + // to OriginSend ensures that the amount spent cannot exceed what the + // user put in the 'send' field. + // Some complex smart contracts require ephemeral script to be used, and + // we expect those scripts to be generated by Dapps and not by user, + // hence we need to put some safeguards. + panic(fmt.Sprintf("invalid banker type %d for ephemeral", bt)) + } + } + m.PanicString("caller is not a realm or an ephemeral") } func X_originSend(m *gno.Machine) (denoms []string, amounts []int64) { diff --git a/gnovm/stdlibs/generated.go b/gnovm/stdlibs/generated.go index 974f1dcabca..f9728f7cc57 100644 --- a/gnovm/stdlibs/generated.go +++ b/gnovm/stdlibs/generated.go @@ -344,14 +344,26 @@ var nativeFuncs = [...]NativeFunc{ }, { "chain/banker", - "assertCallerIsRealm", - []gno.FieldTypeExpr{}, + "assertCallerIsRealmOrEphemeral", + []gno.FieldTypeExpr{ + {NameExpr: *gno.Nx("p0"), Type: gno.X("uint8")}, + }, []gno.FieldTypeExpr{}, true, func(m *gno.Machine) { - libs_chain_banker.X_assertCallerIsRealm( - m, + b := m.LastBlock() + var ( + p0 uint8 + rp0 = reflect.ValueOf(&p0).Elem() ) + + tv0 := b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV + tv0.DeepFill(m.Store) + gno.Gno2GoValue(tv0, rp0) + + libs_chain_banker.X_assertCallerIsRealmOrEphemeral( + m, + p0) }, }, { diff --git a/tm2/pkg/sdk/params/handler.go b/tm2/pkg/sdk/params/handler.go index 10c5ba7c470..c9ce054453f 100644 --- a/tm2/pkg/sdk/params/handler.go +++ b/tm2/pkg/sdk/params/handler.go @@ -53,7 +53,7 @@ func (bh paramsHandler) Query(ctx sdk.Context, req abci.RequestQuery) (res abci. std.ErrUnknownRequest(fmt.Sprintf("module not registered: %q", module))) return } - val := bh.params.GetRaw(ctx, rest) + val := bh.params.GetBytes(ctx, rest) res.Data = val return diff --git a/tm2/pkg/sdk/params/handler_test.go b/tm2/pkg/sdk/params/handler_test.go index cc9f6581005..eaaf9572e89 100644 --- a/tm2/pkg/sdk/params/handler_test.go +++ b/tm2/pkg/sdk/params/handler_test.go @@ -4,12 +4,13 @@ import ( "strings" "testing" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" bft "github.com/gnolang/gno/tm2/pkg/bft/types" "github.com/gnolang/gno/tm2/pkg/sdk" tu "github.com/gnolang/gno/tm2/pkg/sdk/testutils" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestInvalidMsg(t *testing.T) { @@ -35,7 +36,7 @@ func TestArbitraryParamsQuery(t *testing.T) { {path: "params/" + dummyModuleName + ":bar_int64", expected: `"-12345"`}, {path: "params/" + dummyModuleName + ":bar_uint64", expected: `"4242"`}, {path: "params/" + dummyModuleName + ":bar_bool", expected: "true"}, - {path: "params/" + dummyModuleName + ":bar_bytes", expected: `"YmF6"`}, + {path: "params/" + dummyModuleName + ":bar_bytes", expected: `baz`}, } for _, tc := range tcs { @@ -78,7 +79,7 @@ func TestModuleParamsQuery(t *testing.T) { {path: "params/params_test:foo/bar.int64", expected: `"-12345"`}, {path: "params/params_test:foo/bar.uint64", expected: `"4242"`}, {path: "params/params_test:foo/bar.bool", expected: "true"}, - {path: "params/params_test:foo/bar.bytes", expected: `"YmF6"`}, + {path: "params/params_test:foo/bar.bytes", expected: `baz`}, } for _, tc := range tcs { diff --git a/tm2/pkg/sdk/params/keeper.go b/tm2/pkg/sdk/params/keeper.go index 91d435738fb..764db0e856b 100644 --- a/tm2/pkg/sdk/params/keeper.go +++ b/tm2/pkg/sdk/params/keeper.go @@ -29,19 +29,17 @@ type ParamsKeeperI interface { GetInt64(ctx sdk.Context, key string, ptr *int64) GetUint64(ctx sdk.Context, key string, ptr *uint64) GetBool(ctx sdk.Context, key string, ptr *bool) - GetBytes(ctx sdk.Context, key string, ptr *[]byte) GetStrings(ctx sdk.Context, key string, ptr *[]string) SetString(ctx sdk.Context, key string, value string) SetInt64(ctx sdk.Context, key string, value int64) SetUint64(ctx sdk.Context, key string, value uint64) SetBool(ctx sdk.Context, key string, value bool) - SetBytes(ctx sdk.Context, key string, value []byte) SetStrings(ctx sdk.Context, key string, value []string) Has(ctx sdk.Context, key string) bool - GetRaw(ctx sdk.Context, key string) []byte - SetRaw(ctx sdk.Context, key string, value []byte) + GetBytes(ctx sdk.Context, key string) []byte + SetBytes(ctx sdk.Context, key string, value []byte) GetStruct(ctx sdk.Context, key string, strctPtr any) SetStruct(ctx sdk.Context, key string, strct any) @@ -126,8 +124,8 @@ func (pk ParamsKeeper) GetUint64(ctx sdk.Context, key string, ptr *uint64) { pk.getIfExists(ctx, key, ptr) } -func (pk ParamsKeeper) GetBytes(ctx sdk.Context, key string, ptr *[]byte) { - pk.getIfExists(ctx, key, ptr) +func (pk ParamsKeeper) GetBytes(ctx sdk.Context, key string) []byte { + return ctx.Store(pk.key).Get(storeKey(key)) } func (pk ParamsKeeper) GetStrings(ctx sdk.Context, key string, ptr *[]string) { @@ -151,23 +149,13 @@ func (pk ParamsKeeper) SetUint64(ctx sdk.Context, key string, value uint64) { } func (pk ParamsKeeper) SetBytes(ctx sdk.Context, key string, value []byte) { - pk.set(ctx, key, value) + ctx.Store(pk.key).Set(storeKey(key), value) } func (pk ParamsKeeper) SetStrings(ctx sdk.Context, key string, value []string) { pk.set(ctx, key, value) } -func (pk ParamsKeeper) GetRaw(ctx sdk.Context, key string) []byte { - stor := ctx.Store(pk.key) - return stor.Get(storeKey(key)) -} - -func (pk ParamsKeeper) SetRaw(ctx sdk.Context, key string, value []byte) { - stor := ctx.Store(pk.key) - stor.Set(storeKey(key), value) -} - func (pk ParamsKeeper) GetStruct(ctx sdk.Context, key string, strctPtr any) { parts := strings.Split(key, ":") if len(parts) != 2 { @@ -299,8 +287,8 @@ func (ppk prefixParamsKeeper) GetBool(ctx sdk.Context, key string, ptr *bool) { ppk.pk.GetBool(ctx, ppk.prefixed(key), ptr) } -func (ppk prefixParamsKeeper) GetBytes(ctx sdk.Context, key string, ptr *[]byte) { - ppk.pk.GetBytes(ctx, ppk.prefixed(key), ptr) +func (ppk prefixParamsKeeper) GetBytes(ctx sdk.Context, key string) []byte { + return ppk.pk.GetBytes(ctx, ppk.prefixed(key)) } func (ppk prefixParamsKeeper) GetStrings(ctx sdk.Context, key string, ptr *[]string) { @@ -335,14 +323,6 @@ func (ppk prefixParamsKeeper) Has(ctx sdk.Context, key string) bool { return ppk.pk.Has(ctx, ppk.prefixed(key)) } -func (ppk prefixParamsKeeper) GetRaw(ctx sdk.Context, key string) []byte { - return ppk.pk.GetRaw(ctx, ppk.prefixed(key)) -} - -func (ppk prefixParamsKeeper) SetRaw(ctx sdk.Context, key string, value []byte) { - ppk.pk.SetRaw(ctx, ppk.prefixed(key), value) -} - func (ppk prefixParamsKeeper) GetStruct(ctx sdk.Context, key string, paramPtr any) { ppk.pk.GetStruct(ctx, ppk.prefixed(key), paramPtr) } diff --git a/tm2/pkg/sdk/params/keeper_test.go b/tm2/pkg/sdk/params/keeper_test.go index c7b97ed1454..137032a6c24 100644 --- a/tm2/pkg/sdk/params/keeper_test.go +++ b/tm2/pkg/sdk/params/keeper_test.go @@ -4,8 +4,9 @@ import ( "reflect" "testing" - "github.com/gnolang/gno/tm2/pkg/amino" "github.com/stretchr/testify/require" + + "github.com/gnolang/gno/tm2/pkg/amino" ) func TestKeeper(t *testing.T) { @@ -44,7 +45,7 @@ func TestKeeper(t *testing.T) { require.NotPanics(t, func() { keeper.GetBool(ctx, "param2", ¶m2) }) require.NotPanics(t, func() { keeper.GetUint64(ctx, "param3", ¶m3) }) require.NotPanics(t, func() { keeper.GetInt64(ctx, "param4", ¶m4) }) - require.NotPanics(t, func() { keeper.GetBytes(ctx, "param5", ¶m5) }) + require.NotPanics(t, func() { param5 = keeper.GetBytes(ctx, "param5") }) require.Equal(t, param1, "foo") require.Equal(t, param2, true) @@ -69,7 +70,7 @@ func TestKeeper(t *testing.T) { require.NotPanics(t, func() { keeper.GetBool(ctx, "param2", ¶m2) }) require.NotPanics(t, func() { keeper.GetUint64(ctx, "param3", ¶m3) }) require.NotPanics(t, func() { keeper.GetInt64(ctx, "param4", ¶m4) }) - require.NotPanics(t, func() { keeper.GetBytes(ctx, "param5", ¶m5) }) + require.NotPanics(t, func() { param5 = keeper.GetBytes(ctx, "param5") }) require.Equal(t, param1, "bar") require.Equal(t, param2, false) diff --git a/tm2/pkg/std/coin.go b/tm2/pkg/std/coin.go index 16c8f8fe7ff..fa05ffe64a5 100644 --- a/tm2/pkg/std/coin.go +++ b/tm2/pkg/std/coin.go @@ -632,7 +632,9 @@ func (coins Coins) Sort() Coins { // Parsing var ( - reDnmString = `[a-z\/][a-z0-9_.:\/]{2,}` + // Denominations can be 3 ~ 128 characters long and support letters, followed by either + // a letter, a number or a separator ('/', ':', '.', '_' or '-'). + reDnmString = `[a-zA-Z][a-zA-Z0-9/:._-]{2,127}` reAmt = `[[:digit:]]+` reSpc = `[[:space:]]*` reDnm = regexp.MustCompile(fmt.Sprintf(`^%s$`, reDnmString)) diff --git a/tm2/pkg/std/coin_test.go b/tm2/pkg/std/coin_test.go index ba3c8f49c54..48c09f445ed 100644 --- a/tm2/pkg/std/coin_test.go +++ b/tm2/pkg/std/coin_test.go @@ -4,9 +4,10 @@ import ( "strings" "testing" - "github.com/gnolang/gno/tm2/pkg/amino" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/gnolang/gno/tm2/pkg/amino" ) var ( @@ -59,15 +60,16 @@ func TestCoinIsValid(t *testing.T) { {Coin{testDenom1, int64(-1)}, false}, {Coin{testDenom1, int64(0)}, true}, {Coin{testDenom1, int64(1)}, true}, - {Coin{"Atom", int64(1)}, false}, + {Coin{"ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2", int64(1)}, true}, + {Coin{"Atom", int64(1)}, true}, {Coin{"a", int64(1)}, false}, {Coin{"a very long coin denom", int64(1)}, false}, - {Coin{"atOm", int64(1)}, false}, + {Coin{"atOm", int64(1)}, true}, {Coin{" ", int64(1)}, false}, } for i, tc := range cases { - require.Equal(t, tc.expectPass, tc.coin.IsValid(), "unexpected result for IsValid, tc #%d", i) + require.Equal(t, tc.expectPass, tc.coin.IsValid(), "unexpected result for IsValid for coin %s tc #%d", tc.coin, i) } }