From 658c26dbef5a11add3c770edfc3dce5d729698ba Mon Sep 17 00:00:00 2001 From: Thomas Bruyelle Date: Wed, 22 Oct 2025 19:04:31 +0200 Subject: [PATCH 1/5] feat(gnokey): add missing -send flag to maketx run Closes #4866 Added a txtar test to ensure that -send flag doesnt affect the user balance when used in `maketx run`. Added similar txtar for `maketx call`, this time the user balance is affected. --- .../testdata/maketx_call_send.txtar | 36 +++++++++++++++++++ .../testdata/{run.txtar => maketx_run.txtar} | 0 .../testdata/maketx_run_send.txtar | 25 +++++++++++++ gno.land/pkg/keyscli/run.go | 16 ++++++++- 4 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 gno.land/pkg/integration/testdata/maketx_call_send.txtar rename gno.land/pkg/integration/testdata/{run.txtar => maketx_run.txtar} (100%) create mode 100644 gno.land/pkg/integration/testdata/maketx_run_send.txtar 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..f67fc5068de --- /dev/null +++ b/gno.land/pkg/integration/testdata/maketx_run_send.txtar @@ -0,0 +1,25 @@ +## start a new node +gnoland start + +## user balance before realm send +gnokey query auth/accounts/$test1_user_addr +stdout '"coins": "10000000000000ugnot"' + +## run script/script.gno with send flag +gnokey maketx run -send 42ugnot -gas-fee 1000000ugnot -gas-wanted 20000000 -broadcast -chainid=tendermint_test test1 $WORK/script/script.gno +stdout 'send: 42ugnot' + +## user balance after realm send +# only reduced by -gas-fee, -send does not affect balance since it the run +# script shares the same address as the user. +gnokey query auth/accounts/$test1_user_addr +stdout '"coins": "9999999000000ugnot"' + +-- script/script.gno -- +package main + +import "chain/banker" + +func main() { + println("send:", banker.OriginSend()) +} 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{ From 6154ab9be4c3a5f20ac540e721abdcfb8d81d2b9 Mon Sep 17 00:00:00 2001 From: Thomas Bruyelle Date: Thu, 23 Oct 2025 11:33:04 +0200 Subject: [PATCH 2/5] feat: allow banker originSend in ephemerals Closes #4865 --- .../testdata/maketx_run_send.txtar | 61 +++++++++++++++++-- gnovm/stdlibs/chain/banker/banker.gno | 4 +- gnovm/stdlibs/chain/banker/banker.go | 25 +++++++- gnovm/stdlibs/generated.go | 20 ++++-- 4 files changed, 96 insertions(+), 14 deletions(-) diff --git a/gno.land/pkg/integration/testdata/maketx_run_send.txtar b/gno.land/pkg/integration/testdata/maketx_run_send.txtar index f67fc5068de..e601536439f 100644 --- a/gno.land/pkg/integration/testdata/maketx_run_send.txtar +++ b/gno.land/pkg/integration/testdata/maketx_run_send.txtar @@ -5,17 +5,36 @@ gnoland start gnokey query auth/accounts/$test1_user_addr stdout '"coins": "10000000000000ugnot"' -## run script/script.gno with send flag -gnokey maketx run -send 42ugnot -gas-fee 1000000ugnot -gas-wanted 20000000 -broadcast -chainid=tendermint_test test1 $WORK/script/script.gno +## 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 it the run -# script shares the same address as the user. +# 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"' --- script/script.gno -- +## 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" @@ -23,3 +42,35 @@ 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/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) }, }, { From 1e7f4cbbae250a57fb0d8d03a8552c5d2deb0ec9 Mon Sep 17 00:00:00 2001 From: Thomas Bruyelle Date: Wed, 12 Nov 2025 16:15:06 +0100 Subject: [PATCH 3/5] feat(coin): allow IBC denoms --- tm2/pkg/std/coin.go | 4 +++- tm2/pkg/std/coin_test.go | 10 ++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) 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) } } From a96e4fc66e4dde161693661466002c1e993dc9e7 Mon Sep 17 00:00:00 2001 From: Thomas Bruyelle Date: Fri, 21 Nov 2025 16:29:32 +0100 Subject: [PATCH 4/5] fix(params): SetBytes should not alter bytes SetBytes is used by the IBC contract to store the commitment bytes. Then the relayer submit the proof of existence of those bytes to the counter party chain, but if the value bytes are different, the proof cannot be verified. This PR updates SetBytes to use directly the store instead of passing by the common `set` method, preventing the value to be aminoJSON encoded. Removed GetRaw/SetRaw since GetBytes/SetBytes can be used as a replacement. --- gno.land/pkg/gnoland/mock_test.go | 7 ++---- tm2/pkg/sdk/params/handler.go | 2 +- tm2/pkg/sdk/params/handler_test.go | 9 ++++---- tm2/pkg/sdk/params/keeper.go | 34 ++++++------------------------ tm2/pkg/sdk/params/keeper_test.go | 7 +++--- 5 files changed, 19 insertions(+), 40 deletions(-) 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/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) From 33602b5bf8cef17584467222c2374d51060980db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CClockwork=E2=80=9D?= Date: Fri, 28 Nov 2025 13:23:53 +0200 Subject: [PATCH 5/5] feat: Enable empty blocks --- contribs/gnodev/README.md | 4 +++ contribs/gnodev/app_config.go | 43 +++++++++++++++++++++++--------- contribs/gnodev/command_local.go | 3 +++ contribs/gnodev/setup_node.go | 2 ++ 4 files changed, 40 insertions(+), 12 deletions(-) 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