From 22796444089c57f93b48a16679ce32864b9afd37 Mon Sep 17 00:00:00 2001 From: piux2 <90544084+piux2@users.noreply.github.com> Date: Thu, 21 Nov 2024 02:21:09 -0800 Subject: [PATCH 1/8] feat: lock token transfer and parameter module --- contribs/gnodev/pkg/dev/node.go | 28 +++-- contribs/gnodev/pkg/dev/node_state.go | 15 ++- examples/gno.land/r/sys/params/gno.mod | 1 - examples/gno.land/r/sys/params/params.gno | 54 --------- .../gno.land/r/sys/params/params_test.gno | 15 --- examples/gno.land/r/sys/params/unlock.gno | 39 +++++++ .../gno.land/r/sys/params/unlock_test.gno | 49 ++++++++ gno.land/cmd/gnoland/start.go | 8 +- .../cmd/gnoland/testdata/transfer_lock.txtar | 54 +++++++++ .../gnoland/testdata/transfer_unlock.txtar | 42 +++++++ gno.land/pkg/gnoland/app.go | 20 ++-- gno.land/pkg/gnoland/app_test.go | 56 +++++---- gno.land/pkg/gnoland/genesis.go | 13 +++ gno.land/pkg/gnoland/mock_test.go | 73 ++++++++++++ gno.land/pkg/gnoland/types.go | 11 +- .../pkg/integration/testing_integration.go | 22 ++-- gno.land/pkg/integration/testing_node.go | 21 ++-- gno.land/pkg/sdk/vm/builtins.go | 52 ++++++++- gno.land/pkg/sdk/vm/builtins_test.go | 61 ++++++++++ gno.land/pkg/sdk/vm/common_test.go | 9 +- gno.land/pkg/sdk/vm/consts.go | 7 +- gno.land/pkg/sdk/vm/genesis.go | 48 ++++++++ gno.land/pkg/sdk/vm/keeper.go | 9 +- gno.land/pkg/sdk/vm/params.go | 76 +++++++++++++ gnovm/stdlibs/std/params.go | 4 +- tm2/pkg/sdk/auth/ante.go | 3 +- tm2/pkg/sdk/auth/consts.go | 3 + tm2/pkg/sdk/auth/genesis.go | 58 ++++++++++ tm2/pkg/sdk/auth/keeper.go | 4 + tm2/pkg/sdk/auth/params.go | 51 ++++++++- tm2/pkg/sdk/auth/test_common.go | 4 + tm2/pkg/sdk/auth/types.go | 2 + tm2/pkg/sdk/bank/common_test.go | 16 ++- tm2/pkg/sdk/bank/consts.go | 3 +- tm2/pkg/sdk/bank/genesis.go | 55 +++++++++ tm2/pkg/sdk/bank/handler.go | 4 +- tm2/pkg/sdk/bank/handler_test.go | 2 +- tm2/pkg/sdk/bank/keeper.go | 106 +++++++++++++++++- tm2/pkg/sdk/bank/keeper_test.go | 49 +++++++- tm2/pkg/sdk/bank/params.go | 73 ++++++++++++ tm2/pkg/sdk/params/handler.go | 12 +- tm2/pkg/sdk/params/keeper.go | 66 ++++++++++- tm2/pkg/sdk/params/keymapper.go | 39 +++++++ tm2/pkg/sdk/params/test_common.go | 4 +- tm2/pkg/std/account.go | 13 +++ tm2/pkg/std/coin.go | 25 ++++- tm2/pkg/std/errors.go | 72 ++++++------ tm2/pkg/std/package.go | 1 + 48 files changed, 1212 insertions(+), 240 deletions(-) delete mode 100644 examples/gno.land/r/sys/params/params.gno delete mode 100644 examples/gno.land/r/sys/params/params_test.gno create mode 100644 examples/gno.land/r/sys/params/unlock.gno create mode 100644 examples/gno.land/r/sys/params/unlock_test.gno create mode 100644 gno.land/cmd/gnoland/testdata/transfer_lock.txtar create mode 100644 gno.land/cmd/gnoland/testdata/transfer_unlock.txtar create mode 100644 gno.land/pkg/sdk/vm/builtins_test.go create mode 100644 gno.land/pkg/sdk/vm/genesis.go create mode 100644 gno.land/pkg/sdk/vm/params.go create mode 100644 tm2/pkg/sdk/auth/genesis.go create mode 100644 tm2/pkg/sdk/bank/genesis.go create mode 100644 tm2/pkg/sdk/bank/params.go create mode 100644 tm2/pkg/sdk/params/keymapper.go diff --git a/contribs/gnodev/pkg/dev/node.go b/contribs/gnodev/pkg/dev/node.go index 9b3f838b8a0..e323524ebcc 100644 --- a/contribs/gnodev/pkg/dev/node.go +++ b/contribs/gnodev/pkg/dev/node.go @@ -116,10 +116,9 @@ func NewDevNode(ctx context.Context, cfg *NodeConfig) (*Node, error) { } // generate genesis state - genesis := gnoland.GnoGenesisState{ - Balances: cfg.BalancesList, - Txs: append(pkgsTxs, cfg.InitialTxs...), - } + genesis := gnoland.DefaultGenState() + genesis.Balances = cfg.BalancesList + genesis.Txs = append(pkgsTxs, cfg.InitialTxs...) if err := devnode.rebuildNode(ctx, genesis); err != nil { return nil, fmt.Errorf("unable to initialize the node: %w", err) @@ -276,10 +275,9 @@ func (n *Node) Reset(ctx context.Context) error { // Append initialTxs txs := append(pkgsTxs, n.initialState...) - genesis := gnoland.GnoGenesisState{ - Balances: n.config.BalancesList, - Txs: txs, - } + genesis := gnoland.DefaultGenState() + genesis.Balances = n.config.BalancesList + genesis.Txs = txs // Reset the node with the new genesis state. err = n.rebuildNode(ctx, genesis) @@ -402,9 +400,10 @@ func (n *Node) rebuildNodeFromState(ctx context.Context) error { return fmt.Errorf("unable to load pkgs: %w", err) } - return n.rebuildNode(ctx, gnoland.GnoGenesisState{ - Balances: n.config.BalancesList, Txs: txs, - }) + genesis := gnoland.DefaultGenState() + genesis.Balances = n.config.BalancesList + genesis.Txs = txs + return n.rebuildNode(ctx, genesis) } state, err := n.getBlockStoreState(ctx) @@ -419,10 +418,9 @@ func (n *Node) rebuildNodeFromState(ctx context.Context) error { } // Create genesis with loaded pkgs + previous state - genesis := gnoland.GnoGenesisState{ - Balances: n.config.BalancesList, - Txs: append(pkgsTxs, state...), - } + genesis := gnoland.DefaultGenState() + genesis.Balances = n.config.BalancesList + genesis.Txs = append(pkgsTxs, state...) // Reset the node with the new genesis state. err = n.rebuildNode(ctx, genesis) diff --git a/contribs/gnodev/pkg/dev/node_state.go b/contribs/gnodev/pkg/dev/node_state.go index 7504580b333..b7b9bd78284 100644 --- a/contribs/gnodev/pkg/dev/node_state.go +++ b/contribs/gnodev/pkg/dev/node_state.go @@ -92,10 +92,9 @@ func (n *Node) MoveBy(ctx context.Context, x int) error { newState := n.state[:newIndex] // Create genesis with loaded pkgs + previous state - genesis := gnoland.GnoGenesisState{ - Balances: n.config.BalancesList, - Txs: append(pkgsTxs, newState...), - } + genesis := gnoland.DefaultGenState() + genesis.Balances = n.config.BalancesList + genesis.Txs = append(pkgsTxs, newState...) // Reset the node with the new genesis state. if err = n.rebuildNode(ctx, genesis); err != nil { @@ -132,10 +131,10 @@ func (n *Node) ExportStateAsGenesis(ctx context.Context) (*bft.GenesisDoc, error // Get current blockstore state doc := *n.Node.GenesisDoc() // copy doc - doc.AppState = gnoland.GnoGenesisState{ - Balances: n.config.BalancesList, - Txs: state, - } + genState := doc.AppState.(gnoland.GnoGenesisState) + genState.Balances = n.config.BalancesList + genState.Txs = state + doc.AppState = genState return &doc, nil } diff --git a/examples/gno.land/r/sys/params/gno.mod b/examples/gno.land/r/sys/params/gno.mod index 4b4c2bf790f..fca12de575f 100644 --- a/examples/gno.land/r/sys/params/gno.mod +++ b/examples/gno.land/r/sys/params/gno.mod @@ -1,5 +1,4 @@ module gno.land/r/sys/params - require ( gno.land/p/demo/dao v0.0.0-latest gno.land/r/gov/dao/bridge v0.0.0-latest diff --git a/examples/gno.land/r/sys/params/params.gno b/examples/gno.land/r/sys/params/params.gno deleted file mode 100644 index fa04c90de3f..00000000000 --- a/examples/gno.land/r/sys/params/params.gno +++ /dev/null @@ -1,54 +0,0 @@ -// Package params provides functions for creating parameter executors that -// interface with the Params Keeper. -// -// This package enables setting various parameter types (such as strings, -// integers, booleans, and byte slices) through the GovDAO proposal mechanism. -// Each function returns an executor that, when called, sets the specified -// parameter in the Params Keeper. -// -// The executors are designed to be used within governance proposals to modify -// parameters dynamically. The integration with the GovDAO allows for parameter -// changes to be proposed and executed in a controlled manner, ensuring that -// modifications are subject to governance processes. -// -// Example usage: -// -// executor := params.NewStringPropExecutor("exampleKey", "exampleValue") -// // This executor can be used in a governance proposal to set the parameter. -package params - -import ( - "std" - - "gno.land/p/demo/dao" - "gno.land/r/gov/dao/bridge" -) - -func NewStringPropExecutor(key string, value string) dao.Executor { - return newPropExecutor(key, func() { std.SetParamString(key, value) }) -} - -func NewInt64PropExecutor(key string, value int64) dao.Executor { - return newPropExecutor(key, func() { std.SetParamInt64(key, value) }) -} - -func NewUint64PropExecutor(key string, value uint64) dao.Executor { - return newPropExecutor(key, func() { std.SetParamUint64(key, value) }) -} - -func NewBoolPropExecutor(key string, value bool) dao.Executor { - return newPropExecutor(key, func() { std.SetParamBool(key, value) }) -} - -func NewBytesPropExecutor(key string, value []byte) dao.Executor { - return newPropExecutor(key, func() { std.SetParamBytes(key, value) }) -} - -func newPropExecutor(key string, fn func()) dao.Executor { - callback := func() error { - fn() - std.Emit("set", "k", key) - return nil - } - return bridge.GovDAO().NewGovDAOExecutor(callback) -} diff --git a/examples/gno.land/r/sys/params/params_test.gno b/examples/gno.land/r/sys/params/params_test.gno deleted file mode 100644 index eaa1ad039d3..00000000000 --- a/examples/gno.land/r/sys/params/params_test.gno +++ /dev/null @@ -1,15 +0,0 @@ -package params - -import "testing" - -// Testing this package is limited because it only contains an `std.Set` method -// without a corresponding `std.Get` method. For comprehensive testing, refer to -// the tests located in the r/gov/dao/ directory, specifically in one of the -// propX_filetest.gno files. - -func TestNewStringPropExecutor(t *testing.T) { - executor := NewStringPropExecutor("foo", "bar") - if executor == nil { - t.Errorf("executor shouldn't be nil") - } -} diff --git a/examples/gno.land/r/sys/params/unlock.gno b/examples/gno.land/r/sys/params/unlock.gno new file mode 100644 index 00000000000..c62c2bf523f --- /dev/null +++ b/examples/gno.land/r/sys/params/unlock.gno @@ -0,0 +1,39 @@ +package params + +import ( + "std" + + "gno.land/p/demo/dao" + "gno.land/r/gov/dao/bridge" +) + +const lockSendKey = "lockSend.bool" +const UnlockSendDesc = "Proposal to unlock the sending functionality for ugnot." +const LockSendDesc = "Proposal to lock the sending functionality for ugnot." + +func ProposeUnlockSend() uint64 { + callback := func() error { + std.SetParamBool(lockSendKey, false) + return nil + } + return propose(callback, UnlockSendDesc) +} + +func ProposeLockSend() uint64 { + callback := func() error { + std.SetParamBool(lockSendKey, true) + return nil + } + return propose(callback, LockSendDesc) +} + +func propose(callback func() error, desc string) uint64 { + // The callback function is executed only after the proposal is voted on + // and approved by the GovDAO. + exe := bridge.GovDAO().NewGovDAOExecutor(callback) + prop := dao.ProposalRequest{ + Description: desc, + Executor: exe, + } + return bridge.GovDAO().Propose(prop) +} diff --git a/examples/gno.land/r/sys/params/unlock_test.gno b/examples/gno.land/r/sys/params/unlock_test.gno new file mode 100644 index 00000000000..4926784edd0 --- /dev/null +++ b/examples/gno.land/r/sys/params/unlock_test.gno @@ -0,0 +1,49 @@ +package params + +import ( + "testing" + + "gno.land/p/demo/dao" + "gno.land/p/demo/simpledao" + "gno.land/p/demo/urequire" + "gno.land/r/gov/dao/bridge" +) + +func TestProUnlockSend(t *testing.T) { + govdao := bridge.GovDAO() + id := ProposeUnlockSend() + p, err := govdao.GetPropStore().ProposalByID(id) + urequire.NoError(t, err) + urequire.Equal(t, UnlockSendDesc, p.Description()) +} + +func TestFailUnlockSend(t *testing.T) { + govdao := bridge.GovDAO() + id := ProposeUnlockSend() + urequire.PanicsWithMessage( + t, + simpledao.ErrProposalNotAccepted.Error(), + func() { + govdao.ExecuteProposal(id) + }, + ) +} + +func TestExeUnlockSend(t *testing.T) { + govdao := bridge.GovDAO() + id := ProposeUnlockSend() + p, err := govdao.GetPropStore().ProposalByID(id) + urequire.NoError(t, err) + urequire.True(t, dao.Active == p.Status()) + + govdao.VoteOnProposal(id, dao.YesVote) + urequire.True(t, dao.Accepted == p.Status()) + urequire.NotPanics( + t, + func() { + govdao.ExecuteProposal(id) + }, + ) + + urequire.True(t, dao.ExecutionSuccessful == p.Status()) +} diff --git a/gno.land/cmd/gnoland/start.go b/gno.land/cmd/gnoland/start.go index 77d7e20b8ef..a420e652810 100644 --- a/gno.land/cmd/gnoland/start.go +++ b/gno.land/cmd/gnoland/start.go @@ -410,10 +410,10 @@ func generateGenesisFile(genesisFile string, pk crypto.PubKey, c *startCfg) erro genesisTxs = append(pkgsTxs, genesisTxs...) // Construct genesis AppState. - gen.AppState = gnoland.GnoGenesisState{ - Balances: balances, - Txs: genesisTxs, - } + defaultGenState := gnoland.DefaultGenState() + defaultGenState.Balances = balances + defaultGenState.Txs = genesisTxs + gen.AppState = defaultGenState // Write genesis state if err := gen.SaveAs(genesisFile); err != nil { diff --git a/gno.land/cmd/gnoland/testdata/transfer_lock.txtar b/gno.land/cmd/gnoland/testdata/transfer_lock.txtar new file mode 100644 index 00000000000..069a4e94a0e --- /dev/null +++ b/gno.land/cmd/gnoland/testdata/transfer_lock.txtar @@ -0,0 +1,54 @@ +## It tests locking token transfers while allowing the payment of gas fees. + +## locking transfer applies to regular acocunts +adduser regular1 + + +loadpkg gno.land/r/demo/wugnot +loadpkg gno.land/r/demo/echo + +## start a new node. +## The -lock-transfer flag is intended for integration testing purposes +## and is not a valid application flag for gnoland. + +gnoland start -lock-transfer + +## User test1 is an unrestricted account specified in the genesis state +gnokey maketx send -send "9999999ugnot" -to $USER_ADDR_regular1 -gas-fee 1ugnot -gas-wanted 10000000 -broadcast -chainid=tendermint_test test1 + +stdout 'OK!' + +## Restricted simple token transfer +! gnokey maketx send -send "9999999ugnot" -to g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5 -gas-fee 1ugnot -gas-wanted 10000000 -broadcast -chainid=tendermint_test regular1 +stderr 'restricted token transfer error' + +## Restricted token transfer by calling a realm deposit function. +! gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func Deposit -gas-fee 1000000ugnot -send "10000ugnot" -gas-wanted 2000000 -broadcast -chainid=tendermint_test regular1 +stderr 'restricted token transfer error' + + +## Restricted token transfer with depositing to a realm package while adding a package. +! gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/bank -deposit "1000ugnot" -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test regular1 +stderr 'restricted token transfer error' + +## paying gas fees to add a package is acceptable. +gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/bank -gas-fee 1000000ugnot -gas-wanted 2500000 -broadcast -chainid=tendermint_test regular1 +stdout 'OK!' + +## paying gas fees to call a realm function is acceptable. +gnokey maketx call -pkgpath gno.land/r/demo/echo -func Render -args "Hello!" -gas-fee 1000000ugnot -gas-wanted 2500000 -broadcast -chainid=tendermint_test regular1 +stdout 'Hello!' + +-- bank.gno -- +package bank +import ( +"std" +) +func Withdraw(denom string, amt int64) string{ + caller := std.GetOrigCaller() + coin := std.Coins{{denom, amt}} + banker := std.GetBanker(std.BankerTypeOrigSend) + pkgaddr := std.GetOrigPkgAddr() + banker.SendCoins(pkgaddr, caller, coin) + return "Withdrawed!" +} diff --git a/gno.land/cmd/gnoland/testdata/transfer_unlock.txtar b/gno.land/cmd/gnoland/testdata/transfer_unlock.txtar new file mode 100644 index 00000000000..d3c3fb66738 --- /dev/null +++ b/gno.land/cmd/gnoland/testdata/transfer_unlock.txtar @@ -0,0 +1,42 @@ +## It tests unlocking token transfers through GovDAO voting +loadpkg gno.land/r/sys/params +loadpkg gno.land/r/gov/dao/v2 + +patchpkg "g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm" "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5" + +adduser regular1 + +## start a new node +gnoland start -lock-transfer + +## User test1 is an unrestricted account specified in the genesis state +gnokey maketx send -send "9999999ugnot" -to $USER_ADDR_regular1 -gas-fee 1ugnot -gas-wanted 10000000 -broadcast -chainid=tendermint_test test1 + +stdout 'OK!' + +## Restricted simple token transfer for a regular account +! gnokey maketx send -send "100ugnot" -to g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5 -gas-fee 1ugnot -gas-wanted 10000000 -broadcast -chainid=tendermint_test regular1 + +stderr 'restricted token transfer error' + +## Submit a proposal to unlock the transfer. When token transfer is locked, only the predefined unrestricted account test1 in the genesis state can +## pay the fee and submit a proposal to unlock the transfer. +gnokey maketx call -pkgpath gno.land/r/sys/params -func ProposeUnlockSend -send 250000000ugnot -gas-fee 1ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1 + +stdout '(0 uint64)' + + +## vote unlock proposal with unrestricted account test1 +gnokey maketx call -pkgpath gno.land/r/gov/dao/v2 -func VoteOnProposal -args 0 -args "YES" -send 250000000ugnot -gas-fee 1ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1 + +stdout 'OK!' + +## vote unlock proposal with unrestricted account test1 +gnokey maketx call -pkgpath gno.land/r/gov/dao/v2 -func ExecuteProposal -args 0 -send 250000000ugnot -gas-fee 1ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1 + +stdout 'OK!' + +## Restricted transfer is unlocked, allowing simple token transfers for regular accounts. +gnokey maketx send -send "100ugnot" -to g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5 -gas-fee 1ugnot -gas-wanted 10000000 -broadcast -chainid=tendermint_test regular1 + +stdout 'OK!' diff --git a/gno.land/pkg/gnoland/app.go b/gno.land/pkg/gnoland/app.go index e0c93f6194f..ca75b584580 100644 --- a/gno.land/pkg/gnoland/app.go +++ b/gno.land/pkg/gnoland/app.go @@ -89,9 +89,14 @@ func NewAppWithOptions(cfg *AppOptions) (abci.Application, error) { baseApp.MountStoreWithDB(baseKey, dbadapter.StoreConstructor, cfg.DB) // Construct keepers. + + km := params.NewPrefixKeyMapper() + km.RegisterPrefix(auth.ParamsPrefixKey) + km.RegisterPrefix(bank.ParamsPrefixKey) + km.RegisterPrefix(vm.ParamsPrefixKey) + paramsKpr := params.NewParamsKeeper(mainKey, km) acctKpr := auth.NewAccountKeeper(mainKey, ProtoGnoAccount) - bankKpr := bank.NewBankKeeper(acctKpr) - paramsKpr := params.NewParamsKeeper(mainKey, "vm") + bankKpr := bank.NewBankKeeper(acctKpr, paramsKpr) vmk := vm.NewVMKeeper(baseKey, mainKey, acctKpr, bankKpr, paramsKpr) vmk.Output = cfg.VMOutput @@ -293,7 +298,7 @@ func (cfg InitChainerConfig) loadAppState(ctx sdk.Context, appState any) ([]abci if !ok { return nil, fmt.Errorf("invalid AppState of type %T", appState) } - + cfg.bankKpr.InitGenesis(ctx, state.Bank) // Apply genesis balances. for _, bal := range state.Balances { acc := cfg.acctKpr.NewAccountWithAddress(ctx, bal.Address) @@ -303,11 +308,10 @@ func (cfg InitChainerConfig) loadAppState(ctx sdk.Context, appState any) ([]abci panic(err) } } - - // Apply genesis params. - for _, param := range state.Params { - param.register(ctx, cfg.paramsKpr) - } + // The account keeper's initial genesis state must be set after genesis areccounts are created. + // We need to set genesis account status in account keeper. + cfg.acctKpr.InitGenesis(ctx, state.Auth) + cfg.vmKpr.InitGenesis(ctx, state.VM) // Replay genesis txs. txResponses := make([]abci.ResponseDeliverTx, 0, len(state.Txs)) diff --git a/gno.land/pkg/gnoland/app_test.go b/gno.land/pkg/gnoland/app_test.go index 999e04b2c4b..2e3d56de282 100644 --- a/gno.land/pkg/gnoland/app_test.go +++ b/gno.land/pkg/gnoland/app_test.go @@ -66,13 +66,15 @@ func TestNewAppWithOptions(t *testing.T) { }, }, }, - Params: []Param{ - {key: "foo", kind: "string", value: "hello"}, - {key: "foo", kind: "int64", value: int64(-42)}, - {key: "foo", kind: "uint64", value: uint64(1337)}, - {key: "foo", kind: "bool", value: true}, - {key: "foo", kind: "bytes", value: []byte{0x48, 0x69, 0x21}}, - }, + /* + Params: []Param{ + {key: "foo", kind: "string", value: "hello"}, + {key: "foo", kind: "int64", value: int64(-42)}, + {key: "foo", kind: "uint64", value: uint64(1337)}, + {key: "foo", kind: "bool", value: true}, + {key: "foo", kind: "bytes", value: []byte{0x48, 0x69, 0x21}}, + }, + */ }, }) require.True(t, resp.IsOK(), "InitChain response: %v", resp) @@ -97,24 +99,27 @@ func TestNewAppWithOptions(t *testing.T) { cres := bapp.Commit() require.NotNil(t, cres) + /* + tcs := []struct { + path string + expectedVal string + }{ + + {"params/vm/foo.string", `"hello"`}, + {"params/vm/foo.int64", `"-42"`}, + {"params/vm/foo.uint64", `"1337"`}, + {"params/vm/foo.bool", `true`}, + {"params/vm/foo.bytes", `"SGkh"`}, // XXX: make this test more readable + } - tcs := []struct { - path string - expectedVal string - }{ - {"params/vm/foo.string", `"hello"`}, - {"params/vm/foo.int64", `"-42"`}, - {"params/vm/foo.uint64", `"1337"`}, - {"params/vm/foo.bool", `true`}, - {"params/vm/foo.bytes", `"SGkh"`}, // XXX: make this test more readable - } - for _, tc := range tcs { - qres := bapp.Query(abci.RequestQuery{ - Path: tc.path, - }) - require.True(t, qres.IsOK()) - assert.Equal(t, qres.Data, []byte(tc.expectedVal)) - } + for _, tc := range tcs { + qres := bapp.Query(abci.RequestQuery{ + Path: tc.path, + }) + require.True(t, qres.IsOK()) + assert.Equal(t, qres.Data, []byte(tc.expectedVal)) + } + */ } func TestNewAppWithOptions_ErrNoDB(t *testing.T) { @@ -210,6 +215,9 @@ func testInitChainerLoadStdlib(t *testing.T, cached bool) { //nolint:thelper cfg := InitChainerConfig{ StdlibDir: stdlibDir, vmKpr: mock, + acctKpr: &mockAuthKeeper{}, + bankKpr: &mockBankKeeper{}, + paramsKpr: &mockParamsKeeper{}, CacheStdlibLoad: cached, } cfg.InitChainer(testCtx, abci.RequestInitChain{ diff --git a/gno.land/pkg/gnoland/genesis.go b/gno.land/pkg/gnoland/genesis.go index ea692bcaf0d..6e296295194 100644 --- a/gno.land/pkg/gnoland/genesis.go +++ b/gno.land/pkg/gnoland/genesis.go @@ -12,6 +12,9 @@ import ( bft "github.com/gnolang/gno/tm2/pkg/bft/types" "github.com/gnolang/gno/tm2/pkg/crypto" osm "github.com/gnolang/gno/tm2/pkg/os" + "github.com/gnolang/gno/tm2/pkg/sdk/auth" + "github.com/gnolang/gno/tm2/pkg/sdk/bank" + "github.com/gnolang/gno/tm2/pkg/std" "github.com/pelletier/go-toml" ) @@ -187,3 +190,13 @@ func LoadPackage(pkg gnomod.Pkg, creator bft.Address, fee std.Fee, deposit std.C return tx, nil } +func DefaultGenState() GnoGenesisState { + gs := GnoGenesisState{ + Balances: []Balance{}, + Txs: []TxWithMetadata{}, + Auth: auth.DefaultGenesisState(), + Bank: bank.DefaultGenesisState(), + VM: vmm.DefaultGenesisState(), + } + return gs +} diff --git a/gno.land/pkg/gnoland/mock_test.go b/gno.land/pkg/gnoland/mock_test.go index 62aecaf5278..5901b2dd9e5 100644 --- a/gno.land/pkg/gnoland/mock_test.go +++ b/gno.land/pkg/gnoland/mock_test.go @@ -4,10 +4,15 @@ import ( "log/slog" "github.com/gnolang/gno/gno.land/pkg/sdk/vm" + "github.com/gnolang/gno/tm2/pkg/crypto" "github.com/gnolang/gno/tm2/pkg/events" "github.com/gnolang/gno/tm2/pkg/log" "github.com/gnolang/gno/tm2/pkg/sdk" + "github.com/gnolang/gno/tm2/pkg/sdk/auth" + "github.com/gnolang/gno/tm2/pkg/sdk/bank" + "github.com/gnolang/gno/tm2/pkg/service" + "github.com/gnolang/gno/tm2/pkg/std" ) type ( @@ -113,6 +118,74 @@ func (m *mockVMKeeper) CommitGnoTransactionStore(ctx sdk.Context) { } } +func (m *mockVMKeeper) InitGenesis(ctx sdk.Context, gs vm.GenesisState) { + // TODO: +} + +type mockBankKeeper struct { +} + +func (m *mockBankKeeper) InputOutputCoins(ctx sdk.Context, inputs []bank.Input, outputs []bank.Output) error { + return nil +} +func (m *mockBankKeeper) SendCoins(ctx sdk.Context, fromAddr crypto.Address, toAddr crypto.Address, amt std.Coins) error { + return nil +} + +func (m *mockBankKeeper) SubtractCoins(ctx sdk.Context, addr crypto.Address, amt std.Coins) (std.Coins, error) { + return nil, nil +} +func (m *mockBankKeeper) AddCoins(ctx sdk.Context, addr crypto.Address, amt std.Coins) (std.Coins, error) { + return nil, nil +} +func (m *mockBankKeeper) SetCoins(ctx sdk.Context, addr crypto.Address, amt std.Coins) error { + return nil +} + +func (m *mockBankKeeper) InitGenesis(ctx sdk.Context, data bank.GenesisState) {} +func (m *mockBankKeeper) GetParams(ctx sdk.Context) bank.Params { return bank.Params{} } +func (m *mockBankKeeper) GetCoins(ctx sdk.Context, addr crypto.Address) std.Coins { return nil } +func (m *mockBankKeeper) HasCoins(ctx sdk.Context, addr crypto.Address, amt std.Coins) bool { + return true +} + +type mockAuthKeeper struct { +} + +func (m *mockAuthKeeper) NewAccountWithAddress(ctx sdk.Context, addr crypto.Address) std.Account { + return nil +} +func (m *mockAuthKeeper) GetAccount(ctx sdk.Context, addr crypto.Address) std.Account { return nil } +func (m *mockAuthKeeper) GetAllAccounts(ctx sdk.Context) []std.Account { return nil } +func (m *mockAuthKeeper) SetAccount(ctx sdk.Context, acc std.Account) {} +func (m *mockAuthKeeper) IterateAccounts(ctx sdk.Context, process func(std.Account) bool) {} +func (m *mockAuthKeeper) InitGenesis(ctx sdk.Context, data auth.GenesisState) {} + +type mockParamsKeeper struct { +} + +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) SetString(ctx sdk.Context, key string, value string) {} +func (m *mockParamsKeeper) SetInt64(ctx sdk.Context, key string, value int64) {} +func (m *mockParamsKeeper) SetUint64(ctx sdk.Context, key string, value uint64) {} +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) Has(ctx sdk.Context, key string) bool { return false } +func (m *mockParamsKeeper) GetRaw(ctx sdk.Context, key string) []byte { return nil } + +func (m *mockParamsKeeper) GetParams(ctx sdk.Context, prefixKey string, key string, target interface{}) (bool, error) { + return true, nil +} +func (m *mockParamsKeeper) SetParams(ctx sdk.Context, prefixKey string, key string, params interface{}) error { + return nil +} + type ( lastBlockHeightDelegate func() int64 loggerDelegate func() *slog.Logger diff --git a/gno.land/pkg/gnoland/types.go b/gno.land/pkg/gnoland/types.go index a5f76fdcef7..221529e01a8 100644 --- a/gno.land/pkg/gnoland/types.go +++ b/gno.land/pkg/gnoland/types.go @@ -7,7 +7,10 @@ import ( "fmt" "os" + "github.com/gnolang/gno/gno.land/pkg/sdk/vm" "github.com/gnolang/gno/tm2/pkg/amino" + "github.com/gnolang/gno/tm2/pkg/sdk/auth" + "github.com/gnolang/gno/tm2/pkg/sdk/bank" "github.com/gnolang/gno/tm2/pkg/std" ) @@ -25,9 +28,11 @@ func ProtoGnoAccount() std.Account { } type GnoGenesisState struct { - Balances []Balance `json:"balances"` - Txs []TxWithMetadata `json:"txs"` - Params []Param `json:"params"` + Balances []Balance `json:"balances"` + Txs []TxWithMetadata `json:"txs"` + Auth auth.GenesisState `json:"auth"` + Bank bank.GenesisState `json:"bank"` + VM vm.GenesisState `json:"vm"` } type TxWithMetadata struct { diff --git a/gno.land/pkg/integration/testing_integration.go b/gno.land/pkg/integration/testing_integration.go index 235b9581ae0..9fcee0a0041 100644 --- a/gno.land/pkg/integration/testing_integration.go +++ b/gno.land/pkg/integration/testing_integration.go @@ -132,11 +132,10 @@ func setupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params { // Track new user balances added via the `adduser` // command and packages added with the `loadpkg` command. // This genesis will be use when node is started. - genesis := &gnoland.GnoGenesisState{ - Balances: LoadDefaultGenesisBalanceFile(t, gnoRootDir), - Params: LoadDefaultGenesisParamFile(t, gnoRootDir), - Txs: []gnoland.TxWithMetadata{}, - } + cfg := TestingMinimalNodeConfig(t, gnoRootDir) + gs := cfg.Genesis.AppState.(gnoland.GnoGenesisState) + gs.Balances = LoadDefaultGenesisBalanceFile(t, gnoRootDir) + genesis := &gs // test1 must be created outside of the loop below because it is already included in genesis so // attempting to recreate results in it getting overwritten and breaking existing tests that @@ -177,6 +176,7 @@ func setupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params { // parse flags fs := flag.NewFlagSet("start", flag.ContinueOnError) nonVal := fs.Bool("non-validator", false, "set up node as a non-validator") + lockTransfer := fs.Bool("lock-transfer", false, "lock transfer ugnot") if err := fs.Parse(args); err != nil { ts.Fatalf("unable to parse `gnoland start` flags: %s", err) } @@ -185,9 +185,10 @@ func setupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params { pkgs := ts.Value(envKeyPkgsLoader).(*pkgsLoader) // grab logger creator := crypto.MustAddressFromString(DefaultAccount_Address) // test1 defaultFee := std.NewFee(50000, std.MustParseCoin(ugnot.ValueString(1000000))) - pkgsTxs, err := pkgs.LoadPackages(creator, defaultFee, nil) - if err != nil { - ts.Fatalf("unable to load packages txs: %s", err) + // we need to define a new err1 otherwise the out err would be shadowed in the "start" case: + pkgsTxs, err1 := pkgs.LoadPackages(creator, defaultFee, nil) + if err1 != nil { + ts.Fatalf("unable to load packages txs: %s", err1) } // Warp up `ts` so we can pass it to other testing method @@ -195,9 +196,12 @@ func setupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params { // Generate config and node cfg := TestingMinimalNodeConfig(t, gnoRootDir) + // add pkg txs to genesis genesis := ts.Value(envKeyGenesis).(*gnoland.GnoGenesisState) genesis.Txs = append(pkgsTxs, genesis.Txs...) - + if *lockTransfer { + genesis.Bank.Params.RestrictedDenoms = []string{"ugnot"} + } // setup genesis state cfg.Genesis.AppState = *genesis if *nonVal { diff --git a/gno.land/pkg/integration/testing_node.go b/gno.land/pkg/integration/testing_node.go index 7e34049d352..50930ef3f8c 100644 --- a/gno.land/pkg/integration/testing_node.go +++ b/gno.land/pkg/integration/testing_node.go @@ -14,6 +14,8 @@ import ( bft "github.com/gnolang/gno/tm2/pkg/bft/types" "github.com/gnolang/gno/tm2/pkg/crypto" "github.com/gnolang/gno/tm2/pkg/db/memdb" + "github.com/gnolang/gno/tm2/pkg/sdk/auth" + "github.com/gnolang/gno/tm2/pkg/sdk/bank" "github.com/gnolang/gno/tm2/pkg/std" "github.com/stretchr/testify/require" ) @@ -58,18 +60,14 @@ func TestingNodeConfig(t TestingTS, gnoroot string, additionalTxs ...gnoland.TxW cfg := TestingMinimalNodeConfig(t, gnoroot) creator := crypto.MustAddressFromString(DefaultAccount_Address) // test1 - - params := LoadDefaultGenesisParamFile(t, gnoroot) balances := LoadDefaultGenesisBalanceFile(t, gnoroot) txs := make([]gnoland.TxWithMetadata, 0) txs = append(txs, LoadDefaultPackages(t, creator, gnoroot)...) txs = append(txs, additionalTxs...) - - cfg.Genesis.AppState = gnoland.GnoGenesisState{ - Balances: balances, - Txs: txs, - Params: params, - } + ggs := cfg.Genesis.AppState.(gnoland.GnoGenesisState) + ggs.Balances = balances + ggs.Txs = txs + cfg.Genesis.AppState = ggs return cfg, creator } @@ -97,6 +95,8 @@ func TestingMinimalNodeConfig(t TestingTS, gnoroot string) *gnoland.InMemoryNode } func DefaultTestingGenesisConfig(t TestingTS, gnoroot string, self crypto.PubKey, tmconfig *tmcfg.Config) *bft.GenesisDoc { + authGen := auth.DefaultGenesisState() + authGen.Params.UnrestrictedAddrs = []crypto.Address{crypto.MustAddressFromString(DefaultAccount_Address)} return &bft.GenesisDoc{ GenesisTime: time.Now(), ChainID: tmconfig.ChainID(), @@ -123,8 +123,9 @@ func DefaultTestingGenesisConfig(t TestingTS, gnoroot string, self crypto.PubKey Amount: std.MustParseCoins(ugnot.ValueString(10_000_000_000_000)), }, }, - Txs: []gnoland.TxWithMetadata{}, - Params: []gnoland.Param{}, + Txs: []gnoland.TxWithMetadata{}, + Auth: authGen, + Bank: bank.DefaultGenesisState(), }, } } diff --git a/gno.land/pkg/sdk/vm/builtins.go b/gno.land/pkg/sdk/vm/builtins.go index 161e459873d..fcdb3e0e090 100644 --- a/gno.land/pkg/sdk/vm/builtins.go +++ b/gno.land/pkg/sdk/vm/builtins.go @@ -1,6 +1,9 @@ package vm import ( + "fmt" + + "github.com/gnolang/gno/gno.land/pkg/gnoland/ugnot" "github.com/gnolang/gno/tm2/pkg/crypto" "github.com/gnolang/gno/tm2/pkg/sdk" "github.com/gnolang/gno/tm2/pkg/std" @@ -60,21 +63,58 @@ func (bnk *SDKBanker) RemoveCoin(b32addr crypto.Bech32Address, denom string, amo // SDKParams type SDKParams struct { - vmk *VMKeeper - ctx sdk.Context + vmk *VMKeeper + ctx sdk.Context + curRealmPath string } +// These are the native function implementations bound with standard libraries in Gno. +// All methods of this struct are not supposed to be called from outside vm/stdlibs/std. func NewSDKParams(vmk *VMKeeper, ctx sdk.Context) *SDKParams { return &SDKParams{ vmk: vmk, ctx: ctx, } } +func (prm *SDKParams) SetString(key, value string) { + prm.assertRealmAccess() + prm.vmk.prmk.SetString(prm.ctx, key, value) +} + +func (prm *SDKParams) SetBool(key string, value bool) { + prm.assertRealmAccess() + realmParamKey := fmt.Sprintf("%s.%s", prm.curRealmPath, lockSendKey) + if key == realmParamKey { + if value == true { // lock sending ugnot + prm.vmk.bank.AddRestrictedDenoms(prm.ctx, ugnot.Denom) + } else { // unlock sending ugnot + prm.vmk.bank.DelRestrictedDenoms(prm.ctx, ugnot.Denom) + } + } + prm.vmk.prmk.SetBool(prm.ctx, key, value) +} + +func (prm *SDKParams) SetInt64(key string, value int64) { + prm.assertRealmAccess() + prm.vmk.prmk.SetInt64(prm.ctx, key, value) +} -func (prm *SDKParams) SetString(key, value string) { prm.vmk.prmk.SetString(prm.ctx, key, value) } -func (prm *SDKParams) SetBool(key string, value bool) { prm.vmk.prmk.SetBool(prm.ctx, key, value) } -func (prm *SDKParams) SetInt64(key string, value int64) { prm.vmk.prmk.SetInt64(prm.ctx, key, value) } func (prm *SDKParams) SetUint64(key string, value uint64) { + prm.assertRealmAccess() prm.vmk.prmk.SetUint64(prm.ctx, key, value) } -func (prm *SDKParams) SetBytes(key string, value []byte) { prm.vmk.prmk.SetBytes(prm.ctx, key, value) } + +func (prm *SDKParams) SetBytes(key string, value []byte) { + prm.assertRealmAccess() + prm.vmk.prmk.SetBytes(prm.ctx, key, value) +} + +func (prm *SDKParams) SetCurRealmPath(realmPath string) { + prm.curRealmPath = realmPath +} + +func (prm *SDKParams) assertRealmAccess() { + if prm.curRealmPath != ParamsRealmPath { + panic(fmt.Sprintf("Set parameters can only be accessed from: %s", ParamsRealmPath)) + } +} diff --git a/gno.land/pkg/sdk/vm/builtins_test.go b/gno.land/pkg/sdk/vm/builtins_test.go new file mode 100644 index 00000000000..cc885560395 --- /dev/null +++ b/gno.land/pkg/sdk/vm/builtins_test.go @@ -0,0 +1,61 @@ +package vm + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestParamsRestricted(t *testing.T) { + env := setupTestEnv() + params := NewSDKParams(env.vmk, env.ctx) + params.SetCurRealmPath("gno.land/r/foo") + + testCases := []struct { + name string + setFunc func() + expectedMsg string + }{ + { + name: "SetString should panic", + setFunc: func() { + params.SetString("name.string", "foo") + }, + expectedMsg: "Set parameters can only be accessed from: " + ParamsRealmPath, + }, + { + name: "SetBool should panic", + setFunc: func() { + params.SetBool("isFoo.bool", true) + }, + expectedMsg: "Set parameters can only be accessed from: " + ParamsRealmPath, + }, + { + name: "SetInt64 should panic", + setFunc: func() { + params.SetInt64("nummber.int64", -100) + }, + expectedMsg: "Set parameters can only be accessed from: " + ParamsRealmPath, + }, + { + name: "SetUint64 should panic", + setFunc: func() { + params.SetUint64("nummber.uint64", 100) + }, + expectedMsg: "Set parameters can only be accessed from: " + ParamsRealmPath, + }, + { + name: "SetBytes should panic", + setFunc: func() { + params.SetBytes("name.bytes", []byte("foo")) + }, + expectedMsg: "Set parameters can only be accessed from: " + ParamsRealmPath, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + require.PanicsWithValue(t, tc.expectedMsg, tc.setFunc, "The panic message did not match the expected value") + }) + } +} diff --git a/gno.land/pkg/sdk/vm/common_test.go b/gno.land/pkg/sdk/vm/common_test.go index 8b1b7d909c1..de09634335a 100644 --- a/gno.land/pkg/sdk/vm/common_test.go +++ b/gno.land/pkg/sdk/vm/common_test.go @@ -21,7 +21,7 @@ import ( type testEnv struct { ctx sdk.Context vmk *VMKeeper - bank bankm.BankKeeper + bank *bankm.BankKeeper acck authm.AccountKeeper vmh vmHandler } @@ -48,8 +48,11 @@ 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) - prmk := paramsm.NewParamsKeeper(iavlCapKey, "params") + km := paramsm.NewPrefixKeyMapper() + prefix := "params_test" + km.RegisterPrefix(prefix) + prmk := paramsm.NewParamsKeeper(iavlCapKey, km) + bank := bankm.NewBankKeeper(acck, prmk) vmk := NewVMKeeper(baseCapKey, iavlCapKey, acck, bank, prmk) mcw := ms.MultiCacheWrap() diff --git a/gno.land/pkg/sdk/vm/consts.go b/gno.land/pkg/sdk/vm/consts.go index 292f34a9d20..493a42c8215 100644 --- a/gno.land/pkg/sdk/vm/consts.go +++ b/gno.land/pkg/sdk/vm/consts.go @@ -1,6 +1,9 @@ package vm const ( - ModuleName = "vm" - RouterKey = ModuleName + ModuleName = "vm" + RouterKey = ModuleName + ParamsPrefixKey = ModuleName + ParamsRealmPath = "gno.land/r/sys/params" + lockSendKey = "lockSend.bool" ) diff --git a/gno.land/pkg/sdk/vm/genesis.go b/gno.land/pkg/sdk/vm/genesis.go new file mode 100644 index 00000000000..29db3e83cc4 --- /dev/null +++ b/gno.land/pkg/sdk/vm/genesis.go @@ -0,0 +1,48 @@ +package vm + +import ( + "github.com/gnolang/gno/tm2/pkg/amino" + "github.com/gnolang/gno/tm2/pkg/sdk" +) + +// GenesisState - all state that must be provided at genesis +type GenesisState struct { + Params Params `json:"params" yaml:"params"` +} + +// NewGenesisState - Create a new genesis state +func NewGenesisState(params Params) GenesisState { + return GenesisState{params} +} + +// DefaultGenesisState - Return a default genesis state +func DefaultGenesisState() GenesisState { + return NewGenesisState(DefaultParams()) +} + +// ValidateGenesis performs basic validation of genesis data returning an +// error for any failed validation criteria. +func ValidateGenesis(data GenesisState) error { + return data.Params.Validate() +} + +// InitGenesis - Init store state from genesis data +func (vm *VMKeeper) InitGenesis(ctx sdk.Context, data GenesisState) { + if amino.DeepEqual(data, GenesisState{}) { + return + } + + if err := ValidateGenesis(data); err != nil { + panic(err) + } + if err := vm.SetParams(ctx, data.Params); err != nil { + panic(err) + } +} + +// ExportGenesis returns a GenesisState for a given context and keeper +func (vm *VMKeeper) ExportGenesis(ctx sdk.Context) GenesisState { + params := vm.GetParams(ctx) + + return NewGenesisState(params) +} diff --git a/gno.land/pkg/sdk/vm/keeper.go b/gno.land/pkg/sdk/vm/keeper.go index 5fa2075b8f7..953e6c42e16 100644 --- a/gno.land/pkg/sdk/vm/keeper.go +++ b/gno.land/pkg/sdk/vm/keeper.go @@ -51,6 +51,7 @@ type VMKeeperI interface { LoadStdlibCached(ctx sdk.Context, stdlibDir string) MakeGnoTransactionStore(ctx sdk.Context) sdk.Context CommitGnoTransactionStore(ctx sdk.Context) + InitGenesis(ctx sdk.Context, data GenesisState) } var _ VMKeeperI = &VMKeeper{} @@ -63,8 +64,9 @@ type VMKeeper struct { baseKey store.StoreKey iavlKey store.StoreKey acck auth.AccountKeeper - bank bank.BankKeeper + bank *bank.BankKeeper prmk params.ParamsKeeper + params Params // cached, the DeliverTx persistent state. gnoStore gno.Store @@ -75,7 +77,7 @@ func NewVMKeeper( baseKey store.StoreKey, iavlKey store.StoreKey, acck auth.AccountKeeper, - bank bank.BankKeeper, + bank *bank.BankKeeper, prmk params.ParamsKeeper, ) *VMKeeper { vmk := &VMKeeper{ @@ -232,8 +234,7 @@ const sysUsersPkgParamPath = "gno.land/r/sys/params.sys.users_pkgpath.string" // 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.params.SysUsersPkg if sysUsersPkg == "" { return nil } diff --git a/gno.land/pkg/sdk/vm/params.go b/gno.land/pkg/sdk/vm/params.go new file mode 100644 index 00000000000..464d9e60a5e --- /dev/null +++ b/gno.land/pkg/sdk/vm/params.go @@ -0,0 +1,76 @@ +package vm + +import ( + "fmt" + "regexp" + "strings" + + "github.com/gnolang/gno/tm2/pkg/amino" + "github.com/gnolang/gno/tm2/pkg/sdk" +) + +const sysUsersPkgDefault = "gno.land/r/sys/users" + +// Params defines the parameters for the bank module. +type Params struct { + SysUsersPkg string `json:"sysusers_pkgpath" yaml:"sysusers_pkgpath"` +} + +// NewParams creates a new Params object +func NewParams(pkgPath string) Params { + return Params{ + SysUsersPkg: pkgPath, + } +} + +// DefaultParams returns a default set of parameters. +func DefaultParams() Params { + return NewParams(sysUsersPkgDefault) +} + +// String implements the stringer interface. +func (p Params) String() string { + var sb strings.Builder + sb.WriteString("Params: \n") + sb.WriteString(fmt.Sprintf("SysUsersPkg: %q\n", p.SysUsersPkg)) + return sb.String() +} + +func (p Params) Validate() error { + rePkgOrRlmPath := regexp.MustCompile(`^gno\.land\/(?:p|r)(?:\/_?[a-z]+[a-z0-9_]*)+$`) + if !rePkgOrRlmPath.MatchString(p.SysUsersPkg) { + return fmt.Errorf("invalid package/realm path %q, failed to match %q", p.SysUsersPkg, rePkgOrRlmPath) + } + return nil +} + +// Equals returns a boolean determining if two Params types are identical. +func (p Params) Equals(p2 Params) bool { + return amino.DeepEqual(p, p2) +} + +func (vm *VMKeeper) SetParams(ctx sdk.Context, params Params) error { + if params.Equals(Params{}) { + return nil + } + if err := params.Validate(); err != nil { + return err + } + vm.params = params + vm.prmk.SetParams(ctx, ModuleName, "p", params) + return nil +} + +func (vm *VMKeeper) GetParams(ctx sdk.Context) Params { + params := &Params{} + + ok, err := vm.prmk.GetParams(ctx, ModuleName, "p", params) + + if !ok { + panic("params key " + ModuleName + " does not exist") + } + if err != nil { + panic(err.Error()) + } + return *params +} diff --git a/gnovm/stdlibs/std/params.go b/gnovm/stdlibs/std/params.go index e21bd9912dd..8fa25e4dedb 100644 --- a/gnovm/stdlibs/std/params.go +++ b/gnovm/stdlibs/std/params.go @@ -19,6 +19,7 @@ type ParamsInterface interface { SetInt64(key string, val int64) SetUint64(key string, val uint64) SetBytes(key string, val []byte) + SetCurRealmPath(curRealmPath string) } func X_setParamString(m *gno.Machine, key, val string) { @@ -66,7 +67,8 @@ func pkey(m *gno.Machine, key string, kind string) string { } } - // decorate key with realm and type. _, rlmPath := currentRealm(m) + GetContext(m).Params.SetCurRealmPath(rlmPath) + // decorate key with realm and type. return fmt.Sprintf("%s.%s", rlmPath, key) } diff --git a/tm2/pkg/sdk/auth/ante.go b/tm2/pkg/sdk/auth/ante.go index 49662b47a55..ac83ec13285 100644 --- a/tm2/pkg/sdk/auth/ante.go +++ b/tm2/pkg/sdk/auth/ante.go @@ -363,7 +363,8 @@ func DeductFees(bank BankKeeperI, ctx sdk.Context, acc std.Account, fees std.Coi )) } - err := bank.SendCoins(ctx, acc.GetAddress(), FeeCollectorAddress(), fees) + // Sending coins is unrestricted to pay for gas fees + err := bank.SendCoinsUnrestricted(ctx, acc.GetAddress(), FeeCollectorAddress(), fees) if err != nil { return abciResult(err) } diff --git a/tm2/pkg/sdk/auth/consts.go b/tm2/pkg/sdk/auth/consts.go index 09bbb15cdbc..f16966e694e 100644 --- a/tm2/pkg/sdk/auth/consts.go +++ b/tm2/pkg/sdk/auth/consts.go @@ -22,6 +22,9 @@ const ( // param key for global account number GlobalAccountNumberKey = "globalAccountNumber" + + // param + ParamsPrefixKey = ModuleName ) // AddressStoreKey turn an address to key used to get it from the account store diff --git a/tm2/pkg/sdk/auth/genesis.go b/tm2/pkg/sdk/auth/genesis.go new file mode 100644 index 00000000000..f4eaf15c67b --- /dev/null +++ b/tm2/pkg/sdk/auth/genesis.go @@ -0,0 +1,58 @@ +package auth + +import ( + "github.com/gnolang/gno/tm2/pkg/amino" + "github.com/gnolang/gno/tm2/pkg/sdk" +) + +// GenesisState - all state that must be provided at genesis +type GenesisState struct { + Params Params `json:"params" yaml:"params"` +} + +// NewGenesisState - Create a new genesis state +func NewGenesisState(params Params) GenesisState { + return GenesisState{params} +} + +// DefaultGenesisState - Return a default genesis state +func DefaultGenesisState() GenesisState { + return NewGenesisState(DefaultParams()) +} + +// ValidateGenesis performs basic validation of genesis data returning an +// error for any failed validation criteria. +func ValidateGenesis(data GenesisState) error { + return data.Params.Validate() +} + +// InitGenesis - Init store state from genesis data +func (ak AccountKeeper) InitGenesis(ctx sdk.Context, data GenesisState) { + if amino.DeepEqual(data, GenesisState{}) { + return + } + + if err := ValidateGenesis(data); err != nil { + panic(err) + } + + // The unrestricted address must have been created as one of the genesis accounts. + // Otherwise, we cannot verify the unrestricted address in the genesis state. + + for _, addr := range data.Params.UnrestrictedAddrs { + acc := ak.GetAccount(ctx, addr) + acc.SetUnrestricted(true) + ak.SetAccount(ctx, acc) + } + + if err := ak.SetParams(ctx, data.Params); err != nil { + panic(err) + } +} + +// ExportGenesis returns a GenesisState for a given context and keeper +func (ak AccountKeeper) ExportGenesis(ctx sdk.Context) GenesisState { + params := ak.GetParams(ctx) + + return NewGenesisState(params) +} diff --git a/tm2/pkg/sdk/auth/keeper.go b/tm2/pkg/sdk/auth/keeper.go index 7669b8ace73..a7167f290a2 100644 --- a/tm2/pkg/sdk/auth/keeper.go +++ b/tm2/pkg/sdk/auth/keeper.go @@ -7,6 +7,7 @@ import ( "github.com/gnolang/gno/tm2/pkg/amino" "github.com/gnolang/gno/tm2/pkg/crypto" "github.com/gnolang/gno/tm2/pkg/sdk" + "github.com/gnolang/gno/tm2/pkg/sdk/params" "github.com/gnolang/gno/tm2/pkg/std" "github.com/gnolang/gno/tm2/pkg/store" ) @@ -18,6 +19,9 @@ type AccountKeeper struct { // The prototypical Account constructor. proto func() std.Account + + // store module parameters + paramk params.ParamsKeeper } // NewAccountKeeper returns a new AccountKeeper that uses go-amino to diff --git a/tm2/pkg/sdk/auth/params.go b/tm2/pkg/sdk/auth/params.go index dfeaa73af71..eebdbbb7c35 100644 --- a/tm2/pkg/sdk/auth/params.go +++ b/tm2/pkg/sdk/auth/params.go @@ -5,6 +5,8 @@ import ( "strings" "github.com/gnolang/gno/tm2/pkg/amino" + "github.com/gnolang/gno/tm2/pkg/crypto" + "github.com/gnolang/gno/tm2/pkg/sdk" ) type AuthParamsContextKey struct{} @@ -20,11 +22,12 @@ const ( // Params defines the parameters for the auth module. type Params struct { - MaxMemoBytes int64 `json:"max_memo_bytes" yaml:"max_memo_bytes"` - TxSigLimit int64 `json:"tx_sig_limit" yaml:"tx_sig_limit"` - TxSizeCostPerByte int64 `json:"tx_size_cost_per_byte" yaml:"tx_size_cost_per_byte"` - SigVerifyCostED25519 int64 `json:"sig_verify_cost_ed25519" yaml:"sig_verify_cost_ed25519"` - SigVerifyCostSecp256k1 int64 `json:"sig_verify_cost_secp256k1" yaml:"sig_verify_cost_secp256k1"` + MaxMemoBytes int64 `json:"max_memo_bytes" yaml:"max_memo_bytes"` + TxSigLimit int64 `json:"tx_sig_limit" yaml:"tx_sig_limit"` + TxSizeCostPerByte int64 `json:"tx_size_cost_per_byte" yaml:"tx_size_cost_per_byte"` + SigVerifyCostED25519 int64 `json:"sig_verify_cost_ed25519" yaml:"sig_verify_cost_ed25519"` + SigVerifyCostSecp256k1 int64 `json:"sig_verify_cost_secp256k1" yaml:"sig_verify_cost_secp256k1"` + UnrestrictedAddrs []crypto.Address `json:"unrestricted_addrs" yaml:"unrestricted_addrs"` } // NewParams creates a new Params object @@ -67,3 +70,41 @@ func (p Params) String() string { sb.WriteString(fmt.Sprintf("SigVerifyCostSecp256k1: %d\n", p.SigVerifyCostSecp256k1)) return sb.String() } + +func (p Params) Validate() error { + if p.TxSigLimit == 0 { + return fmt.Errorf("invalid tx signature limit: %d", p.TxSigLimit) + } + if p.SigVerifyCostED25519 == 0 { + return fmt.Errorf("invalid ED25519 signature verification cost: %d", p.SigVerifyCostED25519) + } + if p.SigVerifyCostSecp256k1 == 0 { + return fmt.Errorf("invalid SECK256k1 signature verification cost: %d", p.SigVerifyCostSecp256k1) + } + if p.TxSizeCostPerByte == 0 { + return fmt.Errorf("invalid tx size cost per byte: %d", p.TxSizeCostPerByte) + } + return nil +} + +func (ak AccountKeeper) SetParams(ctx sdk.Context, params Params) error { + if err := params.Validate(); err != nil { + return err + } + ak.paramk.SetParams(ctx, ModuleName, "p", params) + return nil +} + +func (ak AccountKeeper) GetParams(ctx sdk.Context) Params { + params := &Params{} + + ok, err := ak.paramk.GetParams(ctx, ModuleName, "p", params) + + if !ok { + panic("params key " + ModuleName + " does not exist") + } + if err != nil { + panic(err.Error()) + } + return *params +} diff --git a/tm2/pkg/sdk/auth/test_common.go b/tm2/pkg/sdk/auth/test_common.go index f833a0b0564..98450da30f4 100644 --- a/tm2/pkg/sdk/auth/test_common.go +++ b/tm2/pkg/sdk/auth/test_common.go @@ -60,6 +60,10 @@ func NewDummyBankKeeper(acck AccountKeeper) DummyBankKeeper { return DummyBankKeeper{acck} } +func (bank DummyBankKeeper) SendCoinsUnrestricted(ctx sdk.Context, fromAddr crypto.Address, toAddr crypto.Address, amt std.Coins) error { + return bank.SendCoins(ctx, fromAddr, toAddr, amt) +} + // SendCoins for the dummy supply keeper func (bank DummyBankKeeper) SendCoins(ctx sdk.Context, fromAddr crypto.Address, toAddr crypto.Address, amt std.Coins) error { fromAcc := bank.acck.GetAccount(ctx, fromAddr) diff --git a/tm2/pkg/sdk/auth/types.go b/tm2/pkg/sdk/auth/types.go index 8bbc5e39e3b..3dabc6c0e8d 100644 --- a/tm2/pkg/sdk/auth/types.go +++ b/tm2/pkg/sdk/auth/types.go @@ -13,6 +13,7 @@ type AccountKeeperI interface { GetAllAccounts(ctx sdk.Context) []std.Account SetAccount(ctx sdk.Context, acc std.Account) IterateAccounts(ctx sdk.Context, process func(std.Account) bool) + InitGenesis(ctx sdk.Context, data GenesisState) } var _ AccountKeeperI = AccountKeeper{} @@ -20,4 +21,5 @@ var _ AccountKeeperI = AccountKeeper{} // Limited interface only needed for auth. type BankKeeperI interface { SendCoins(ctx sdk.Context, fromAddr crypto.Address, toAddr crypto.Address, amt std.Coins) error + SendCoinsUnrestricted(ctx sdk.Context, fromAddr crypto.Address, toAddr crypto.Address, amt std.Coins) error } diff --git a/tm2/pkg/sdk/bank/common_test.go b/tm2/pkg/sdk/bank/common_test.go index 95b93157165..485134afb00 100644 --- a/tm2/pkg/sdk/bank/common_test.go +++ b/tm2/pkg/sdk/bank/common_test.go @@ -9,15 +9,17 @@ import ( "github.com/gnolang/gno/tm2/pkg/sdk" "github.com/gnolang/gno/tm2/pkg/sdk/auth" + "github.com/gnolang/gno/tm2/pkg/sdk/params" "github.com/gnolang/gno/tm2/pkg/std" "github.com/gnolang/gno/tm2/pkg/store" "github.com/gnolang/gno/tm2/pkg/store/iavl" ) type testEnv struct { - ctx sdk.Context - bank BankKeeper - acck auth.AccountKeeper + ctx sdk.Context + bank *BankKeeper + acck auth.AccountKeeper + paramk params.ParamsKeeper } func setupTestEnv() testEnv { @@ -33,8 +35,10 @@ func setupTestEnv() testEnv { acck := auth.NewAccountKeeper( authCapKey, std.ProtoBaseAccount, ) + km := params.NewPrefixKeyMapper() + km.RegisterPrefix(ParamsPrefixKey) + paramk := params.NewParamsKeeper(authCapKey, km) + bank := NewBankKeeper(acck, paramk) - bank := NewBankKeeper(acck) - - return testEnv{ctx: ctx, bank: bank, acck: acck} + return testEnv{ctx: ctx, bank: bank, acck: acck, paramk: paramk} } diff --git a/tm2/pkg/sdk/bank/consts.go b/tm2/pkg/sdk/bank/consts.go index 4284a44c940..233f9c04191 100644 --- a/tm2/pkg/sdk/bank/consts.go +++ b/tm2/pkg/sdk/bank/consts.go @@ -1,5 +1,6 @@ package bank const ( - ModuleName = "bank" + ModuleName = "bank" + ParamsPrefixKey = ModuleName ) diff --git a/tm2/pkg/sdk/bank/genesis.go b/tm2/pkg/sdk/bank/genesis.go new file mode 100644 index 00000000000..2354effc1e4 --- /dev/null +++ b/tm2/pkg/sdk/bank/genesis.go @@ -0,0 +1,55 @@ +package bank + +import ( + "github.com/gnolang/gno/tm2/pkg/amino" + "github.com/gnolang/gno/tm2/pkg/sdk" +) + +// GenesisState - all state that must be provided at genesis +type GenesisState struct { + Params Params `json:"params" yaml:"params"` +} + +// NewGenesisState - Create a new genesis state +func NewGenesisState(params Params) GenesisState { + return GenesisState{params} +} + +// DefaultGenesisState - Return a default genesis state +func DefaultGenesisState() GenesisState { + return NewGenesisState(DefaultParams()) +} + +// ValidateGenesis performs basic validation of genesis data returning an +// error for any failed validation criteria. +func ValidateGenesis(data GenesisState) error { + return data.Params.Validate() +} + +// InitGenesis - Init store state from genesis data +func (bank *BankKeeper) InitGenesis(ctx sdk.Context, data GenesisState) { + if amino.DeepEqual(data, GenesisState{}) { + return + } + if err := ValidateGenesis(data); err != nil { + panic(err) + } + + bank.restrictedDenoms = map[string]struct{}{} + + params := data.Params + for _, denom := range params.RestrictedDenoms { + bank.restrictedDenoms[denom] = struct{}{} + } + + if err := bank.SetParams(ctx, data.Params); err != nil { + panic(err) + } +} + +// ExportGenesis returns a GenesisState for a given context and keeper +func (bank *BankKeeper) ExportGenesis(ctx sdk.Context) GenesisState { + params := bank.GetParams(ctx) + + return NewGenesisState(params) +} diff --git a/tm2/pkg/sdk/bank/handler.go b/tm2/pkg/sdk/bank/handler.go index b151af11064..08fe6fb6e5d 100644 --- a/tm2/pkg/sdk/bank/handler.go +++ b/tm2/pkg/sdk/bank/handler.go @@ -12,11 +12,11 @@ import ( ) type bankHandler struct { - bank BankKeeper + bank *BankKeeper } // NewHandler returns a handler for "bank" type messages. -func NewHandler(bank BankKeeper) bankHandler { +func NewHandler(bank *BankKeeper) bankHandler { return bankHandler{ bank: bank, } diff --git a/tm2/pkg/sdk/bank/handler_test.go b/tm2/pkg/sdk/bank/handler_test.go index 85fc68fc304..619285d8e4f 100644 --- a/tm2/pkg/sdk/bank/handler_test.go +++ b/tm2/pkg/sdk/bank/handler_test.go @@ -18,7 +18,7 @@ import ( func TestInvalidMsg(t *testing.T) { t.Parallel() - h := NewHandler(BankKeeper{}) + h := NewHandler(&BankKeeper{}) res := h.Process(sdk.NewContext(sdk.RunTxModeDeliver, nil, &bft.Header{ChainID: "test-chain"}, nil), tu.NewTestMsg()) require.False(t, res.IsOK()) require.True(t, strings.Contains(res.Log, "unrecognized bank message type")) diff --git a/tm2/pkg/sdk/bank/keeper.go b/tm2/pkg/sdk/bank/keeper.go index f98e6b3e225..3361584deef 100644 --- a/tm2/pkg/sdk/bank/keeper.go +++ b/tm2/pkg/sdk/bank/keeper.go @@ -7,6 +7,7 @@ import ( "github.com/gnolang/gno/tm2/pkg/crypto" "github.com/gnolang/gno/tm2/pkg/sdk" "github.com/gnolang/gno/tm2/pkg/sdk/auth" + "github.com/gnolang/gno/tm2/pkg/sdk/params" "github.com/gnolang/gno/tm2/pkg/std" ) @@ -21,9 +22,12 @@ type BankKeeperI interface { SubtractCoins(ctx sdk.Context, addr crypto.Address, amt std.Coins) (std.Coins, error) AddCoins(ctx sdk.Context, addr crypto.Address, amt std.Coins) (std.Coins, error) SetCoins(ctx sdk.Context, addr crypto.Address, amt std.Coins) error + + InitGenesis(ctx sdk.Context, data GenesisState) + GetParams(ctx sdk.Context) Params } -var _ BankKeeperI = BankKeeper{} +var _ BankKeeperI = &BankKeeper{} // BankKeeper only allows transfers between accounts without the possibility of // creating coins. It implements the BankKeeperI interface. @@ -31,14 +35,71 @@ type BankKeeper struct { ViewKeeper acck auth.AccountKeeper + // The keeper used to store parameters + paramk params.ParamsKeeper + params Params + restrictedDenoms map[string]struct{} } // NewBankKeeper returns a new BankKeeper. -func NewBankKeeper(acck auth.AccountKeeper) BankKeeper { - return BankKeeper{ +func NewBankKeeper(acck auth.AccountKeeper, pk params.ParamsKeeper) *BankKeeper { + rdm := map[string]struct{}{} + + params := DefaultParams() + for _, denom := range params.RestrictedDenoms { + rdm[denom] = struct{}{} + } + return &BankKeeper{ ViewKeeper: NewViewKeeper(acck), acck: acck, + paramk: pk, + params: params, + // Store restricted denoms in a map's keys for fast + // comparison when filtering out restricted denoms from a send message. + restrictedDenoms: rdm, + } +} + +func (bank *BankKeeper) AddRestrictedDenoms(ctx sdk.Context, restrictedDenoms ...string) { + if len(restrictedDenoms) == 0 { + return + } + for _, denom := range restrictedDenoms { + bank.restrictedDenoms[denom] = struct{}{} + } + if len(bank.params.RestrictedDenoms) == 0 { + bank.params.RestrictedDenoms = restrictedDenoms + bank.SetParams(ctx, bank.params) } + bank.updateParams(ctx) +} + +func (bank *BankKeeper) DelRestrictedDenoms(ctx sdk.Context, restrictedDenoms ...string) { + for denom := range bank.restrictedDenoms { + delete(bank.restrictedDenoms, denom) + } + bank.updateParams(ctx) +} + +func (bank *BankKeeper) DelAllRestrictedDenoms(ctx sdk.Context) { + bank.restrictedDenoms = map[string]struct{}{} + bank.updateParams(ctx) +} + +func (bank *BankKeeper) RestrictedDenoms(ctx sdk.Context) []string { + // covert restricted denoms map into a slice + denoms := make([]string, 0, len(bank.restrictedDenoms)) + for d := range bank.restrictedDenoms { + denoms = append(denoms, d) + } + return denoms +} + +func (bank *BankKeeper) updateParams(ctx sdk.Context) { + params := bank.GetParams(ctx) + params.RestrictedDenoms = bank.RestrictedDenoms(ctx) + bank.params = params + bank.SetParams(ctx, params) } // InputOutputCoins handles a list of inputs and outputs @@ -50,6 +111,9 @@ func (bank BankKeeper) InputOutputCoins(ctx sdk.Context, inputs []Input, outputs } for _, in := range inputs { + if !bank.canSendCoins(ctx, in.Address, in.Coins) { + return std.RestrictedTransferError{} + } _, err := bank.SubtractCoins(ctx, in.Address, in.Coins) if err != nil { return err @@ -84,8 +148,42 @@ func (bank BankKeeper) InputOutputCoins(ctx sdk.Context, inputs []Input, outputs return nil } -// SendCoins moves coins from one account to another +// canSendCoins returns true if the coins can be sent without violating any restriction. +func (bank BankKeeper) canSendCoins(ctx sdk.Context, addr crypto.Address, amt std.Coins) bool { + if len(bank.restrictedDenoms) == 0 { + // No restrictions. + return true + } + if amt.ContainOneOfDenom(bank.restrictedDenoms) { + if acc := bank.acck.GetAccount(ctx, addr); acc != nil && acc.IsRestricted() { + return false + } + } + return true +} + +// SendCoins moves coins from one account to another, restrction could be applied func (bank BankKeeper) SendCoins(ctx sdk.Context, fromAddr crypto.Address, toAddr crypto.Address, amt std.Coins) error { + return bank.sendCoins(ctx, fromAddr, toAddr, amt, true) +} + +func (bank BankKeeper) SendCoinsUnrestricted(ctx sdk.Context, fromAddr crypto.Address, toAddr crypto.Address, amt std.Coins) error { + return bank.sendCoins(ctx, fromAddr, toAddr, amt, false) +} + +func (bank BankKeeper) sendCoins( + ctx sdk.Context, + fromAddr crypto.Address, + toAddr crypto.Address, + amt std.Coins, + wRestriction bool, // with restriction +) error { + // read restricted boolean value from param.IsRestrictedTransfer() + // canSendCoins is true until they have agreed to the waiver + + if wRestriction && !bank.canSendCoins(ctx, fromAddr, amt) { + return std.RestrictedTransferError{} + } _, err := bank.SubtractCoins(ctx, fromAddr, amt) if err != nil { return err diff --git a/tm2/pkg/sdk/bank/keeper_test.go b/tm2/pkg/sdk/bank/keeper_test.go index df2039a682c..ed910afc30f 100644 --- a/tm2/pkg/sdk/bank/keeper_test.go +++ b/tm2/pkg/sdk/bank/keeper_test.go @@ -95,7 +95,7 @@ func TestBankKeeper(t *testing.T) { env := setupTestEnv() ctx := env.ctx - bank := NewBankKeeper(env.acck) + bank := env.bank addr := crypto.AddressFromPreimage([]byte("addr1")) addr2 := crypto.AddressFromPreimage([]byte("addr2")) @@ -136,6 +136,53 @@ func TestBankKeeper(t *testing.T) { err = bank.SendCoins(ctx, addr, addr2, sdk.Coins{sdk.Coin{Denom: "FOOCOIN", Amount: -5}}) require.Error(t, err) } +func TestBankKeeperWithRestrictions(t *testing.T) { + env := setupTestEnv() + ctx := env.ctx + + bankKeeper := env.bank + bankKeeper.AddRestrictedDenoms(ctx, "foocoin") + addr := crypto.AddressFromPreimage([]byte("addr1")) + addr2 := crypto.AddressFromPreimage([]byte("addr2")) + acc := env.acck.NewAccountWithAddress(ctx, addr) + + // Test GetCoins/SetCoins + env.acck.SetAccount(ctx, acc) + require.True(t, bankKeeper.GetCoins(ctx, addr).IsEqual(std.NewCoins())) + + env.bank.SetCoins(ctx, addr, std.NewCoins(std.NewCoin("foocoin", 10))) + require.True(t, bankKeeper.GetCoins(ctx, addr).IsEqual(std.NewCoins(std.NewCoin("foocoin", 10)))) + + // Test HasCoins + require.True(t, bankKeeper.HasCoins(ctx, addr, std.NewCoins(std.NewCoin("foocoin", 10)))) + require.True(t, bankKeeper.HasCoins(ctx, addr, std.NewCoins(std.NewCoin("foocoin", 5)))) + require.False(t, bankKeeper.HasCoins(ctx, addr, std.NewCoins(std.NewCoin("foocoin", 15)))) + require.False(t, bankKeeper.HasCoins(ctx, addr, std.NewCoins(std.NewCoin("barcoin", 5)))) + + env.bank.SetCoins(ctx, addr, std.NewCoins(std.NewCoin("foocoin", 15))) + + // Test sending coins restricted to locked accounts. + err := bankKeeper.SendCoins(ctx, addr, addr2, std.NewCoins(std.NewCoin("foocoin", 5))) + require.ErrorIs(t, err, std.RestrictedTransferError{}, "expected restricted transfer error, got %v", err) + require.True(t, bankKeeper.GetCoins(ctx, addr).IsEqual(std.NewCoins(std.NewCoin("foocoin", 15)))) + require.True(t, bankKeeper.GetCoins(ctx, addr2).IsEqual(std.NewCoins(std.NewCoin("foocoin", 0)))) + + // Test sending coins unrestricted to locked accounts. + env.bank.AddCoins(ctx, addr, std.NewCoins(std.NewCoin("barcoin", 30))) + err = bankKeeper.SendCoins(ctx, addr, addr2, std.NewCoins(std.NewCoin("barcoin", 10))) + require.NoError(t, err) + require.True(t, bankKeeper.GetCoins(ctx, addr).IsEqual(std.NewCoins(std.NewCoin("barcoin", 20), std.NewCoin("foocoin", 15)))) + require.True(t, bankKeeper.GetCoins(ctx, addr2).IsEqual(std.NewCoins(std.NewCoin("barcoin", 10)))) + + // Remove the restrictions + bankKeeper.DelAllRestrictedDenoms(ctx) + // Test sending coins restricted to locked accounts. + err = bankKeeper.SendCoins(ctx, addr, addr2, std.NewCoins(std.NewCoin("foocoin", 5))) + require.NoError(t, err) + require.True(t, bankKeeper.GetCoins(ctx, addr).IsEqual(std.NewCoins(std.NewCoin("barcoin", 20), std.NewCoin("foocoin", 10)))) + require.True(t, bankKeeper.GetCoins(ctx, addr2).IsEqual(std.NewCoins(std.NewCoin("barcoin", 10), std.NewCoin("foocoin", 5)))) + +} func TestViewKeeper(t *testing.T) { t.Parallel() diff --git a/tm2/pkg/sdk/bank/params.go b/tm2/pkg/sdk/bank/params.go new file mode 100644 index 00000000000..2a9d12eccc7 --- /dev/null +++ b/tm2/pkg/sdk/bank/params.go @@ -0,0 +1,73 @@ +package bank + +import ( + "fmt" + "strings" + + "github.com/gnolang/gno/tm2/pkg/sdk" + "github.com/gnolang/gno/tm2/pkg/std" +) + +type BankParamsContextKey struct{} + +// Params defines the parameters for the bank module. +type Params struct { + RestrictedDenoms []string `json:"restriced_denoms" yaml:"restriced_denoms"` +} + +// NewParams creates a new Params object +func NewParams(restDenoms []string) Params { + return Params{ + RestrictedDenoms: restDenoms, + } +} + +// DefaultParams returns a default set of parameters. +func DefaultParams() Params { + return NewParams([]string{}) +} + +// String implements the stringer interface. +func (p Params) String() string { + var sb strings.Builder + sb.WriteString("Params: \n") + sb.WriteString(fmt.Sprintf("RestrictedDenom: %q\n", p.RestrictedDenoms)) + return sb.String() +} + +func (p *Params) Validate() error { + for _, denom := range p.RestrictedDenoms { + err := std.ValidateDenom(denom) + if err != nil { + return fmt.Errorf("invalid restricted denom: %s", denom) + } + } + return nil +} + +func (bank BankKeeper) SetParams(ctx sdk.Context, params Params) error { + if len(params.RestrictedDenoms) == 0 { + return nil + } + if err := params.Validate(); err != nil { + return err + } + bank.params = params + bank.paramk.SetParams(ctx, ModuleName, "p", params) + + return nil +} + +func (bank BankKeeper) GetParams(ctx sdk.Context) Params { + params := &Params{} + + ok, err := bank.paramk.GetParams(ctx, ModuleName, "p", params) + + if !ok { + panic("params key " + ModuleName + " does not exist") + } + if err != nil { + panic(err.Error()) + } + return *params +} diff --git a/tm2/pkg/sdk/params/handler.go b/tm2/pkg/sdk/params/handler.go index b662fc06c58..7ff58403638 100644 --- a/tm2/pkg/sdk/params/handler.go +++ b/tm2/pkg/sdk/params/handler.go @@ -28,14 +28,14 @@ func (bh paramsHandler) Process(ctx sdk.Context, msg std.Msg) sdk.Result { // Query func (bh paramsHandler) Query(ctx sdk.Context, req abci.RequestQuery) (res abci.ResponseQuery) { - switch secondPart(req.Path) { - case bh.params.prefix: + prefix := secondPart(req.Path) + if bh.params.PrefixExist(prefix) { return bh.queryParam(ctx, req) - default: - res = sdk.ABCIResponseQueryFromError( - std.ErrUnknownRequest("unknown params query endpoint")) - return } + res = sdk.ABCIResponseQueryFromError( + std.ErrUnknownRequest("unknown params query endpoint")) + return + } // queryParam returns param for a key. diff --git a/tm2/pkg/sdk/params/keeper.go b/tm2/pkg/sdk/params/keeper.go index 523e8d54f69..087033769b5 100644 --- a/tm2/pkg/sdk/params/keeper.go +++ b/tm2/pkg/sdk/params/keeper.go @@ -30,6 +30,9 @@ type ParamsKeeperI interface { Has(ctx sdk.Context, key string) bool GetRaw(ctx sdk.Context, key string) []byte + GetParams(ctx sdk.Context, prefixKey string, key string, target interface{}) (bool, error) + SetParams(ctx sdk.Context, prefixKey string, key string, params interface{}) error + // XXX: ListKeys? } @@ -37,15 +40,16 @@ var _ ParamsKeeperI = ParamsKeeper{} // global paramstore Keeper. type ParamsKeeper struct { - key store.StoreKey - prefix string + key store.StoreKey + prefix string + prefixKeyMapper PrefixKeyMapper } // NewParamsKeeper returns a new ParamsKeeper. -func NewParamsKeeper(key store.StoreKey, prefix string) ParamsKeeper { +func NewParamsKeeper(key store.StoreKey, pkm PrefixKeyMapper) ParamsKeeper { return ParamsKeeper{ - key: key, - prefix: prefix, + key: key, + prefixKeyMapper: pkm, } } @@ -115,6 +119,58 @@ func (pk ParamsKeeper) SetBytes(ctx sdk.Context, key string, value []byte) { pk.set(ctx, key, value) } +// GetParam gets a param value from the global param store. +func (pk ParamsKeeper) GetParams(ctx sdk.Context, prefixKey string, key string, target interface{}) (bool, error) { + vk, err := pk.valueStoreKey(prefixKey, key) + if err != nil { + return false, err + } + + stor := ctx.Store(pk.key) + + bz := stor.Get(vk) + if bz == nil { + return false, nil + } + + return true, amino.Unmarshal(bz, target) +} + +// SetParam sets a param value to the global param store. +func (pk ParamsKeeper) SetParams(ctx sdk.Context, prefixKey string, key string, param interface{}) error { + vk, err := pk.valueStoreKey(prefixKey, key) + if err != nil { + return err + } + + bz, err := amino.Marshal(param) + if err != nil { + return err + } + + stor := ctx.Store(pk.key) + + stor.Set(vk, bz) + return nil +} + +func (pk ParamsKeeper) valueStoreKey(prefix string, key string) ([]byte, error) { + prefix, err := pk.prefixKeyMapper.Map(prefix) + if err != nil { + return nil, err + } + return append([]byte(prefix), []byte(key)...), nil +} + +func (pk ParamsKeeper) PrefixExist(prefix string) bool { + _, err := pk.prefixKeyMapper.Map(prefix) + + if err != nil { + return false + } + return true +} + func (pk ParamsKeeper) getIfExists(ctx sdk.Context, key string, ptr interface{}) { stor := ctx.Store(pk.key) bz := stor.Get([]byte(key)) diff --git a/tm2/pkg/sdk/params/keymapper.go b/tm2/pkg/sdk/params/keymapper.go new file mode 100644 index 00000000000..933a8b70798 --- /dev/null +++ b/tm2/pkg/sdk/params/keymapper.go @@ -0,0 +1,39 @@ +package params + +import "fmt" + +// KeyMapper is used to map one key string to another. +type KeyMapper interface { + // Map does a transformation on an input key to produce the key + // appropriate for accessing a param keeper's storage instance. + Map(key string) (string, error) +} + +var _ KeyMapper = PrefixKeyMapper{} + +type PrefixKeyMapper struct { + keyMap map[string]string +} + +func NewPrefixKeyMapper() PrefixKeyMapper { + return PrefixKeyMapper{ + keyMap: map[string]string{}, + } +} + +func (pkm PrefixKeyMapper) RegisterPrefix(prefix string) { + pkm.keyMap[prefix] = "/" + prefix + "/" +} + +func (pkm PrefixKeyMapper) IsExist(prefix string) bool { + _, ok := pkm.keyMap[prefix] + return ok +} + +func (pkm PrefixKeyMapper) Map(prefix string) (string, error) { + v, ok := pkm.keyMap[prefix] + if !ok { + return "", fmt.Errorf("prefix %s does not exisit", prefix) + } + return v, nil +} diff --git a/tm2/pkg/sdk/params/test_common.go b/tm2/pkg/sdk/params/test_common.go index 8243ee867de..5423bc8c98d 100644 --- a/tm2/pkg/sdk/params/test_common.go +++ b/tm2/pkg/sdk/params/test_common.go @@ -23,8 +23,10 @@ func setupTestEnv() testEnv { ms.MountStoreWithDB(paramsCapKey, iavl.StoreConstructor, db) ms.LoadLatestVersion() + km := NewPrefixKeyMapper() prefix := "params_test" - keeper := NewParamsKeeper(paramsCapKey, prefix) + km.RegisterPrefix(prefix) + keeper := NewParamsKeeper(paramsCapKey, km) ctx := sdk.NewContext(sdk.RunTxModeDeliver, ms, &bft.Header{Height: 1, ChainID: "test-chain-id"}, log.NewNoopLogger()) // XXX: context key? diff --git a/tm2/pkg/std/account.go b/tm2/pkg/std/account.go index c70f43d22e9..d1c2876610d 100644 --- a/tm2/pkg/std/account.go +++ b/tm2/pkg/std/account.go @@ -34,6 +34,9 @@ type Account interface { GetCoins() Coins SetCoins(Coins) error + IsRestricted() bool + SetUnrestricted(bool) + // Ensure that account implements stringer String() string } @@ -49,6 +52,7 @@ type BaseAccount struct { PubKey crypto.PubKey `json:"public_key" yaml:"public_key"` AccountNumber uint64 `json:"account_number" yaml:"account_number"` Sequence uint64 `json:"sequence" yaml:"sequence"` + Unrestricted bool `json:"unrestricted" yaml:"unrestricted"` } // NewBaseAccount creates a new BaseAccount object @@ -151,3 +155,12 @@ func (acc *BaseAccount) SetSequence(seq uint64) error { acc.Sequence = seq return nil } + +func (acc *BaseAccount) IsRestricted() bool { + return !acc.Unrestricted +} + +// IsLocked returns true if the account is locked. +func (acc *BaseAccount) SetUnrestricted(unrestricted bool) { + acc.Unrestricted = unrestricted +} diff --git a/tm2/pkg/std/coin.go b/tm2/pkg/std/coin.go index 4f36757efc0..101530e75c1 100644 --- a/tm2/pkg/std/coin.go +++ b/tm2/pkg/std/coin.go @@ -63,7 +63,7 @@ func (coin Coin) String() string { // validate returns an error if the Coin has a negative amount or if // the denom is invalid. func validate(denom string, amount int64) error { - if err := validateDenom(denom); err != nil { + if err := ValidateDenom(denom); err != nil { return err } @@ -229,7 +229,7 @@ func (coins Coins) IsValid() bool { case 0: return true case 1: - if err := validateDenom(coins[0].Denom); err != nil { + if err := ValidateDenom(coins[0].Denom); err != nil { return false } return coins[0].IsPositive() @@ -328,6 +328,21 @@ func (coins Coins) AddUnsafe(coinsB Coins) Coins { } } +// ContainOneOfDenom check if a Coins instance contains a denom in the provided denomos +func (coins Coins) ContainOneOfDenom(denoms map[string]struct{}) bool { + if len(denoms) == 0 { + return false + } + + for _, coin := range coins { + if _, ok := denoms[coin.Denom]; ok { + return true + } + } + + return false +} + // DenomsSubsetOf returns true if receiver's denom set // is subset of coinsB's denoms. func (coins Coins) DenomsSubsetOf(coinsB Coins) bool { @@ -623,7 +638,7 @@ var ( reCoin = regexp.MustCompile(fmt.Sprintf(`^(%s)%s(%s)$`, reAmt, reSpc, reDnmString)) ) -func validateDenom(denom string) error { +func ValidateDenom(denom string) error { if !reDnm.MatchString(denom) { return fmt.Errorf("invalid denom: %s", denom) } @@ -631,7 +646,7 @@ func validateDenom(denom string) error { } func mustValidateDenom(denom string) { - if err := validateDenom(denom); err != nil { + if err := ValidateDenom(denom); err != nil { panic(err) } } @@ -661,7 +676,7 @@ func ParseCoin(coinStr string) (coin Coin, err error) { return Coin{}, errors.Wrap(err, "failed to parse coin amount: %s", amountStr) } - if err := validateDenom(denomStr); err != nil { + if err := ValidateDenom(denomStr); err != nil { return Coin{}, fmt.Errorf("invalid denom cannot contain upper case characters or spaces: %w", err) } diff --git a/tm2/pkg/std/errors.go b/tm2/pkg/std/errors.go index 715b27b3eb4..48bd93be1de 100644 --- a/tm2/pkg/std/errors.go +++ b/tm2/pkg/std/errors.go @@ -14,43 +14,45 @@ func (abciError) AssertABCIError() {} type InternalError struct{ abciError } type ( - TxDecodeError struct{ abciError } - InvalidSequenceError struct{ abciError } - UnauthorizedError struct{ abciError } - InsufficientFundsError struct{ abciError } - UnknownRequestError struct{ abciError } - InvalidAddressError struct{ abciError } - UnknownAddressError struct{ abciError } - InvalidPubKeyError struct{ abciError } - InsufficientCoinsError struct{ abciError } - InvalidCoinsError struct{ abciError } - InvalidGasWantedError struct{ abciError } - OutOfGasError struct{ abciError } - MemoTooLargeError struct{ abciError } - InsufficientFeeError struct{ abciError } - TooManySignaturesError struct{ abciError } - NoSignaturesError struct{ abciError } - GasOverflowError struct{ abciError } + TxDecodeError struct{ abciError } + InvalidSequenceError struct{ abciError } + UnauthorizedError struct{ abciError } + InsufficientFundsError struct{ abciError } + UnknownRequestError struct{ abciError } + InvalidAddressError struct{ abciError } + UnknownAddressError struct{ abciError } + InvalidPubKeyError struct{ abciError } + InsufficientCoinsError struct{ abciError } + InvalidCoinsError struct{ abciError } + InvalidGasWantedError struct{ abciError } + OutOfGasError struct{ abciError } + MemoTooLargeError struct{ abciError } + InsufficientFeeError struct{ abciError } + TooManySignaturesError struct{ abciError } + NoSignaturesError struct{ abciError } + GasOverflowError struct{ abciError } + RestrictedTransferError struct{ abciError } ) -func (e InternalError) Error() string { return "internal error" } -func (e TxDecodeError) Error() string { return "tx decode error" } -func (e InvalidSequenceError) Error() string { return "invalid sequence error" } -func (e UnauthorizedError) Error() string { return "unauthorized error" } -func (e InsufficientFundsError) Error() string { return "insufficient funds error" } -func (e UnknownRequestError) Error() string { return "unknown request error" } -func (e InvalidAddressError) Error() string { return "invalid address error" } -func (e UnknownAddressError) Error() string { return "unknown address error" } -func (e InvalidPubKeyError) Error() string { return "invalid pubkey error" } -func (e InsufficientCoinsError) Error() string { return "insufficient coins error" } -func (e InvalidCoinsError) Error() string { return "invalid coins error" } -func (e InvalidGasWantedError) Error() string { return "invalid gas wanted" } -func (e OutOfGasError) Error() string { return "out of gas error" } -func (e MemoTooLargeError) Error() string { return "memo too large error" } -func (e InsufficientFeeError) Error() string { return "insufficient fee error" } -func (e TooManySignaturesError) Error() string { return "too many signatures error" } -func (e NoSignaturesError) Error() string { return "no signatures error" } -func (e GasOverflowError) Error() string { return "gas overflow error" } +func (e InternalError) Error() string { return "internal error" } +func (e TxDecodeError) Error() string { return "tx decode error" } +func (e InvalidSequenceError) Error() string { return "invalid sequence error" } +func (e UnauthorizedError) Error() string { return "unauthorized error" } +func (e InsufficientFundsError) Error() string { return "insufficient funds error" } +func (e UnknownRequestError) Error() string { return "unknown request error" } +func (e InvalidAddressError) Error() string { return "invalid address error" } +func (e UnknownAddressError) Error() string { return "unknown address error" } +func (e InvalidPubKeyError) Error() string { return "invalid pubkey error" } +func (e InsufficientCoinsError) Error() string { return "insufficient coins error" } +func (e InvalidCoinsError) Error() string { return "invalid coins error" } +func (e InvalidGasWantedError) Error() string { return "invalid gas wanted" } +func (e OutOfGasError) Error() string { return "out of gas error" } +func (e MemoTooLargeError) Error() string { return "memo too large error" } +func (e InsufficientFeeError) Error() string { return "insufficient fee error" } +func (e TooManySignaturesError) Error() string { return "too many signatures error" } +func (e NoSignaturesError) Error() string { return "no signatures error" } +func (e GasOverflowError) Error() string { return "gas overflow error" } +func (e RestrictedTransferError) Error() string { return "restricted token transfer error" } // NOTE also update pkg/std/package.go registrations. diff --git a/tm2/pkg/std/package.go b/tm2/pkg/std/package.go index 3f71c69f0ce..ca8ed805680 100644 --- a/tm2/pkg/std/package.go +++ b/tm2/pkg/std/package.go @@ -32,4 +32,5 @@ var Package = amino.RegisterPackage(amino.NewPackage( TooManySignaturesError{}, "TooManySignaturesError", NoSignaturesError{}, "NoSignaturesError", GasOverflowError{}, "GasOverflowError", + RestrictedTransferError{}, "RestrictedTransferError", )) From c29392664d2c54013558ff3fd5c4179158f2c295 Mon Sep 17 00:00:00 2001 From: piux2 <90544084+piux2@users.noreply.github.com> Date: Thu, 21 Nov 2024 02:45:12 -0800 Subject: [PATCH 2/8] chores: ufmt --- gno.land/pkg/gnoland/genesis.go | 1 + gno.land/pkg/gnoland/mock_test.go | 13 +++++++------ gno.land/pkg/sdk/vm/builtins.go | 1 + gnovm/tests/file.go | 1 + tm2/pkg/sdk/bank/keeper_test.go | 2 +- tm2/pkg/sdk/params/handler.go | 1 - tm2/pkg/sdk/params/keeper.go | 1 - 7 files changed, 11 insertions(+), 9 deletions(-) diff --git a/gno.land/pkg/gnoland/genesis.go b/gno.land/pkg/gnoland/genesis.go index 6e296295194..50dfa467198 100644 --- a/gno.land/pkg/gnoland/genesis.go +++ b/gno.land/pkg/gnoland/genesis.go @@ -190,6 +190,7 @@ func LoadPackage(pkg gnomod.Pkg, creator bft.Address, fee std.Fee, deposit std.C return tx, nil } + func DefaultGenState() GnoGenesisState { gs := GnoGenesisState{ Balances: []Balance{}, diff --git a/gno.land/pkg/gnoland/mock_test.go b/gno.land/pkg/gnoland/mock_test.go index 5901b2dd9e5..788b5ee9cad 100644 --- a/gno.land/pkg/gnoland/mock_test.go +++ b/gno.land/pkg/gnoland/mock_test.go @@ -122,12 +122,12 @@ func (m *mockVMKeeper) InitGenesis(ctx sdk.Context, gs vm.GenesisState) { // TODO: } -type mockBankKeeper struct { -} +type mockBankKeeper struct{} func (m *mockBankKeeper) InputOutputCoins(ctx sdk.Context, inputs []bank.Input, outputs []bank.Output) error { return nil } + func (m *mockBankKeeper) SendCoins(ctx sdk.Context, fromAddr crypto.Address, toAddr crypto.Address, amt std.Coins) error { return nil } @@ -135,9 +135,11 @@ func (m *mockBankKeeper) SendCoins(ctx sdk.Context, fromAddr crypto.Address, toA func (m *mockBankKeeper) SubtractCoins(ctx sdk.Context, addr crypto.Address, amt std.Coins) (std.Coins, error) { return nil, nil } + func (m *mockBankKeeper) AddCoins(ctx sdk.Context, addr crypto.Address, amt std.Coins) (std.Coins, error) { return nil, nil } + func (m *mockBankKeeper) SetCoins(ctx sdk.Context, addr crypto.Address, amt std.Coins) error { return nil } @@ -149,8 +151,7 @@ func (m *mockBankKeeper) HasCoins(ctx sdk.Context, addr crypto.Address, amt std. return true } -type mockAuthKeeper struct { -} +type mockAuthKeeper struct{} func (m *mockAuthKeeper) NewAccountWithAddress(ctx sdk.Context, addr crypto.Address) std.Account { return nil @@ -161,8 +162,7 @@ func (m *mockAuthKeeper) SetAccount(ctx sdk.Context, acc std.Account) func (m *mockAuthKeeper) IterateAccounts(ctx sdk.Context, process func(std.Account) bool) {} func (m *mockAuthKeeper) InitGenesis(ctx sdk.Context, data auth.GenesisState) {} -type mockParamsKeeper struct { -} +type mockParamsKeeper struct{} func (m *mockParamsKeeper) GetString(ctx sdk.Context, key string, ptr *string) {} func (m *mockParamsKeeper) GetInt64(ctx sdk.Context, key string, ptr *int64) {} @@ -182,6 +182,7 @@ func (m *mockParamsKeeper) GetRaw(ctx sdk.Context, key string) []byte { return n func (m *mockParamsKeeper) GetParams(ctx sdk.Context, prefixKey string, key string, target interface{}) (bool, error) { return true, nil } + func (m *mockParamsKeeper) SetParams(ctx sdk.Context, prefixKey string, key string, params interface{}) error { return nil } diff --git a/gno.land/pkg/sdk/vm/builtins.go b/gno.land/pkg/sdk/vm/builtins.go index fcdb3e0e090..aa947b6f254 100644 --- a/gno.land/pkg/sdk/vm/builtins.go +++ b/gno.land/pkg/sdk/vm/builtins.go @@ -76,6 +76,7 @@ func NewSDKParams(vmk *VMKeeper, ctx sdk.Context) *SDKParams { ctx: ctx, } } + func (prm *SDKParams) SetString(key, value string) { prm.assertRealmAccess() prm.vmk.prmk.SetString(prm.ctx, key, value) diff --git a/gnovm/tests/file.go b/gnovm/tests/file.go index 98dbab6ac0e..528336d7bba 100644 --- a/gnovm/tests/file.go +++ b/gnovm/tests/file.go @@ -647,6 +647,7 @@ func (tp *testParams) SetBytes(key string, val []byte) { /* noop */ } func (tp *testParams) SetInt64(key string, val int64) { /* noop */ } func (tp *testParams) SetUint64(key string, val uint64) { /* noop */ } func (tp *testParams) SetString(key string, val string) { /* noop */ } +func (tp *testParams) SetCurRealmPath(path string) { /* noop */ } // ---------------------------------------- // testBanker diff --git a/tm2/pkg/sdk/bank/keeper_test.go b/tm2/pkg/sdk/bank/keeper_test.go index ed910afc30f..35a9f8b0f05 100644 --- a/tm2/pkg/sdk/bank/keeper_test.go +++ b/tm2/pkg/sdk/bank/keeper_test.go @@ -136,6 +136,7 @@ func TestBankKeeper(t *testing.T) { err = bank.SendCoins(ctx, addr, addr2, sdk.Coins{sdk.Coin{Denom: "FOOCOIN", Amount: -5}}) require.Error(t, err) } + func TestBankKeeperWithRestrictions(t *testing.T) { env := setupTestEnv() ctx := env.ctx @@ -181,7 +182,6 @@ func TestBankKeeperWithRestrictions(t *testing.T) { require.NoError(t, err) require.True(t, bankKeeper.GetCoins(ctx, addr).IsEqual(std.NewCoins(std.NewCoin("barcoin", 20), std.NewCoin("foocoin", 10)))) require.True(t, bankKeeper.GetCoins(ctx, addr2).IsEqual(std.NewCoins(std.NewCoin("barcoin", 10), std.NewCoin("foocoin", 5)))) - } func TestViewKeeper(t *testing.T) { diff --git a/tm2/pkg/sdk/params/handler.go b/tm2/pkg/sdk/params/handler.go index 7ff58403638..893f4563d84 100644 --- a/tm2/pkg/sdk/params/handler.go +++ b/tm2/pkg/sdk/params/handler.go @@ -35,7 +35,6 @@ func (bh paramsHandler) Query(ctx sdk.Context, req abci.RequestQuery) (res abci. res = sdk.ABCIResponseQueryFromError( std.ErrUnknownRequest("unknown params query endpoint")) return - } // queryParam returns param for a key. diff --git a/tm2/pkg/sdk/params/keeper.go b/tm2/pkg/sdk/params/keeper.go index 087033769b5..cc02b3b9236 100644 --- a/tm2/pkg/sdk/params/keeper.go +++ b/tm2/pkg/sdk/params/keeper.go @@ -164,7 +164,6 @@ func (pk ParamsKeeper) valueStoreKey(prefix string, key string) ([]byte, error) func (pk ParamsKeeper) PrefixExist(prefix string) bool { _, err := pk.prefixKeyMapper.Map(prefix) - if err != nil { return false } From 3591b39b7fd65232225859ee7dffc20ae6cbbd56 Mon Sep 17 00:00:00 2001 From: piux2 <90544084+piux2@users.noreply.github.com> Date: Sun, 8 Dec 2024 20:58:47 -0800 Subject: [PATCH 3/8] addressed comments --- examples/gno.land/r/sys/params/unlock.gno | 8 +++-- gno.land/pkg/gnoland/app.go | 6 ++-- .../pkg/integration/testing_integration.go | 8 ++--- gno.land/pkg/sdk/vm/builtins.go | 6 ++-- gno.land/pkg/sdk/vm/common_test.go | 2 +- gno.land/pkg/sdk/vm/params.go | 13 +++++--- tm2/pkg/sdk/auth/genesis.go | 3 ++ tm2/pkg/sdk/auth/keeper.go | 13 ++++---- tm2/pkg/sdk/auth/params.go | 30 ++++++++++--------- tm2/pkg/sdk/auth/test_common.go | 8 +++-- tm2/pkg/sdk/bank/common_test.go | 6 ++-- tm2/pkg/sdk/bank/keeper.go | 26 +++++++++------- tm2/pkg/sdk/bank/params.go | 8 +++-- tm2/pkg/sdk/params/handler.go | 2 +- tm2/pkg/sdk/params/keeper.go | 6 ++-- tm2/pkg/sdk/params/keymapper.go | 12 ++------ 16 files changed, 88 insertions(+), 69 deletions(-) diff --git a/examples/gno.land/r/sys/params/unlock.gno b/examples/gno.land/r/sys/params/unlock.gno index c62c2bf523f..8cc158ac9dd 100644 --- a/examples/gno.land/r/sys/params/unlock.gno +++ b/examples/gno.land/r/sys/params/unlock.gno @@ -7,9 +7,11 @@ import ( "gno.land/r/gov/dao/bridge" ) -const lockSendKey = "lockSend.bool" -const UnlockSendDesc = "Proposal to unlock the sending functionality for ugnot." -const LockSendDesc = "Proposal to lock the sending functionality for ugnot." +const ( + lockSendKey = "lockSend.bool" + UnlockSendDesc = "Proposal to unlock the sending functionality for ugnot." + LockSendDesc = "Proposal to lock the sending functionality for ugnot." +) func ProposeUnlockSend() uint64 { callback := func() error { diff --git a/gno.land/pkg/gnoland/app.go b/gno.land/pkg/gnoland/app.go index ca75b584580..61b9cff7a77 100644 --- a/gno.land/pkg/gnoland/app.go +++ b/gno.land/pkg/gnoland/app.go @@ -95,7 +95,7 @@ func NewAppWithOptions(cfg *AppOptions) (abci.Application, error) { km.RegisterPrefix(bank.ParamsPrefixKey) km.RegisterPrefix(vm.ParamsPrefixKey) paramsKpr := params.NewParamsKeeper(mainKey, km) - acctKpr := auth.NewAccountKeeper(mainKey, ProtoGnoAccount) + acctKpr := auth.NewAccountKeeper(mainKey, paramsKpr, ProtoGnoAccount) bankKpr := bank.NewBankKeeper(acctKpr, paramsKpr) vmk := vm.NewVMKeeper(baseKey, mainKey, acctKpr, bankKpr, paramsKpr) vmk.Output = cfg.VMOutput @@ -119,7 +119,7 @@ func NewAppWithOptions(cfg *AppOptions) (abci.Application, error) { ) { // Override auth params. ctx = ctx. - WithValue(auth.AuthParamsContextKey{}, auth.DefaultParams()) + WithValue(auth.AuthParamsContextKey{}, acctKpr.GetParams(ctx)) // Continue on with default auth ante handler. newCtx, res, abort = authAnteHandler(ctx, tx, simulate) return @@ -308,7 +308,7 @@ func (cfg InitChainerConfig) loadAppState(ctx sdk.Context, appState any) ([]abci panic(err) } } - // The account keeper's initial genesis state must be set after genesis areccounts are created. + // The account keeper's initial genesis state must be set after genesis accounts are created. // We need to set genesis account status in account keeper. cfg.acctKpr.InitGenesis(ctx, state.Auth) cfg.vmKpr.InitGenesis(ctx, state.VM) diff --git a/gno.land/pkg/integration/testing_integration.go b/gno.land/pkg/integration/testing_integration.go index 9fcee0a0041..bb5f00ade41 100644 --- a/gno.land/pkg/integration/testing_integration.go +++ b/gno.land/pkg/integration/testing_integration.go @@ -185,10 +185,10 @@ func setupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params { pkgs := ts.Value(envKeyPkgsLoader).(*pkgsLoader) // grab logger creator := crypto.MustAddressFromString(DefaultAccount_Address) // test1 defaultFee := std.NewFee(50000, std.MustParseCoin(ugnot.ValueString(1000000))) - // we need to define a new err1 otherwise the out err would be shadowed in the "start" case: - pkgsTxs, err1 := pkgs.LoadPackages(creator, defaultFee, nil) - if err1 != nil { - ts.Fatalf("unable to load packages txs: %s", err1) + // we need to define a new loadErr otherwise the out err would be shadowed in the "start" case: + pkgsTxs, loadErr := pkgs.LoadPackages(creator, defaultFee, nil) + if loadErr != nil { + ts.Fatalf("unable to load packages txs: %s", loadErr) } // Warp up `ts` so we can pass it to other testing method diff --git a/gno.land/pkg/sdk/vm/builtins.go b/gno.land/pkg/sdk/vm/builtins.go index aa947b6f254..e243bfce992 100644 --- a/gno.land/pkg/sdk/vm/builtins.go +++ b/gno.land/pkg/sdk/vm/builtins.go @@ -63,8 +63,10 @@ func (bnk *SDKBanker) RemoveCoin(b32addr crypto.Bech32Address, denom string, amo // SDKParams type SDKParams struct { - vmk *VMKeeper - ctx sdk.Context + vmk *VMKeeper + ctx sdk.Context + // The curRealmPath is used to track the current realm accessing the SDKParams from the VM. + // It serves as a safeguard to control access from the VM. curRealmPath string } diff --git a/gno.land/pkg/sdk/vm/common_test.go b/gno.land/pkg/sdk/vm/common_test.go index de09634335a..7972e7fd315 100644 --- a/gno.land/pkg/sdk/vm/common_test.go +++ b/gno.land/pkg/sdk/vm/common_test.go @@ -47,11 +47,11 @@ func _setupTestEnv(cacheStdlibs bool) testEnv { ms.LoadLatestVersion() ctx := sdk.NewContext(sdk.RunTxModeDeliver, ms, &bft.Header{ChainID: "test-chain-id"}, log.NewNoopLogger()) - acck := authm.NewAccountKeeper(iavlCapKey, std.ProtoBaseAccount) km := paramsm.NewPrefixKeyMapper() prefix := "params_test" km.RegisterPrefix(prefix) prmk := paramsm.NewParamsKeeper(iavlCapKey, km) + acck := authm.NewAccountKeeper(iavlCapKey, prmk, std.ProtoBaseAccount) bank := bankm.NewBankKeeper(acck, prmk) vmk := NewVMKeeper(baseCapKey, iavlCapKey, acck, bank, prmk) diff --git a/gno.land/pkg/sdk/vm/params.go b/gno.land/pkg/sdk/vm/params.go index 464d9e60a5e..2a09f126cbb 100644 --- a/gno.land/pkg/sdk/vm/params.go +++ b/gno.land/pkg/sdk/vm/params.go @@ -9,7 +9,11 @@ import ( "github.com/gnolang/gno/tm2/pkg/sdk" ) -const sysUsersPkgDefault = "gno.land/r/sys/users" +const ( + sysUsersPkgDefault = "gno.land/r/sys/users" + + paramsKey = "p" +) // Params defines the parameters for the bank module. type Params struct { @@ -37,6 +41,7 @@ func (p Params) String() string { } func (p Params) Validate() error { + // XXX: This line is copied from gnovm/memfile.go. Should we instead export rePkgOrRlmPath with a getter method from gnovm? rePkgOrRlmPath := regexp.MustCompile(`^gno\.land\/(?:p|r)(?:\/_?[a-z]+[a-z0-9_]*)+$`) if !rePkgOrRlmPath.MatchString(p.SysUsersPkg) { return fmt.Errorf("invalid package/realm path %q, failed to match %q", p.SysUsersPkg, rePkgOrRlmPath) @@ -57,14 +62,14 @@ func (vm *VMKeeper) SetParams(ctx sdk.Context, params Params) error { return err } vm.params = params - vm.prmk.SetParams(ctx, ModuleName, "p", params) - return nil + err := vm.prmk.SetParams(ctx, ModuleName, paramsKey, params) + return err } func (vm *VMKeeper) GetParams(ctx sdk.Context) Params { params := &Params{} - ok, err := vm.prmk.GetParams(ctx, ModuleName, "p", params) + ok, err := vm.prmk.GetParams(ctx, ModuleName, paramsKey, params) if !ok { panic("params key " + ModuleName + " does not exist") diff --git a/tm2/pkg/sdk/auth/genesis.go b/tm2/pkg/sdk/auth/genesis.go index f4eaf15c67b..dde1082d778 100644 --- a/tm2/pkg/sdk/auth/genesis.go +++ b/tm2/pkg/sdk/auth/genesis.go @@ -29,6 +29,9 @@ func ValidateGenesis(data GenesisState) error { // InitGenesis - Init store state from genesis data func (ak AccountKeeper) InitGenesis(ctx sdk.Context, data GenesisState) { if amino.DeepEqual(data, GenesisState{}) { + if err := ak.SetParams(ctx, DefaultParams()); err != nil { + panic(err) + } return } diff --git a/tm2/pkg/sdk/auth/keeper.go b/tm2/pkg/sdk/auth/keeper.go index a7167f290a2..dd0a8925b68 100644 --- a/tm2/pkg/sdk/auth/keeper.go +++ b/tm2/pkg/sdk/auth/keeper.go @@ -17,21 +17,22 @@ type AccountKeeper struct { // The (unexposed) key used to access the store from the Context. key store.StoreKey - // The prototypical Account constructor. - proto func() std.Account - // store module parameters paramk params.ParamsKeeper + + // The prototypical Account constructor. + proto func() std.Account } // NewAccountKeeper returns a new AccountKeeper that uses go-amino to // (binary) encode and decode concrete std.Accounts. func NewAccountKeeper( - key store.StoreKey, proto func() std.Account, + key store.StoreKey, pk params.ParamsKeeper, proto func() std.Account, ) AccountKeeper { return AccountKeeper{ - key: key, - proto: proto, + key: key, + paramk: pk, + proto: proto, } } diff --git a/tm2/pkg/sdk/auth/params.go b/tm2/pkg/sdk/auth/params.go index eebdbbb7c35..b8471f3f631 100644 --- a/tm2/pkg/sdk/auth/params.go +++ b/tm2/pkg/sdk/auth/params.go @@ -18,6 +18,8 @@ const ( DefaultTxSizeCostPerByte int64 = 10 DefaultSigVerifyCostED25519 int64 = 590 DefaultSigVerifyCostSecp256k1 int64 = 1000 + + paramsKey = "p" ) // Params defines the parameters for the auth module. @@ -50,13 +52,13 @@ func (p Params) Equals(p2 Params) bool { // DefaultParams returns a default set of parameters. func DefaultParams() Params { - return Params{ - MaxMemoBytes: DefaultMaxMemoBytes, - TxSigLimit: DefaultTxSigLimit, - TxSizeCostPerByte: DefaultTxSizeCostPerByte, - SigVerifyCostED25519: DefaultSigVerifyCostED25519, - SigVerifyCostSecp256k1: DefaultSigVerifyCostSecp256k1, - } + return NewParams( + DefaultMaxMemoBytes, + DefaultTxSigLimit, + DefaultTxSizeCostPerByte, + DefaultSigVerifyCostED25519, + DefaultSigVerifyCostSecp256k1, + ) } // String implements the stringer interface. @@ -72,16 +74,16 @@ func (p Params) String() string { } func (p Params) Validate() error { - if p.TxSigLimit == 0 { + if p.TxSigLimit <= 0 { return fmt.Errorf("invalid tx signature limit: %d", p.TxSigLimit) } - if p.SigVerifyCostED25519 == 0 { + if p.SigVerifyCostED25519 <= 0 { return fmt.Errorf("invalid ED25519 signature verification cost: %d", p.SigVerifyCostED25519) } - if p.SigVerifyCostSecp256k1 == 0 { + if p.SigVerifyCostSecp256k1 <= 0 { return fmt.Errorf("invalid SECK256k1 signature verification cost: %d", p.SigVerifyCostSecp256k1) } - if p.TxSizeCostPerByte == 0 { + if p.TxSizeCostPerByte <= 0 { return fmt.Errorf("invalid tx size cost per byte: %d", p.TxSizeCostPerByte) } return nil @@ -91,14 +93,14 @@ func (ak AccountKeeper) SetParams(ctx sdk.Context, params Params) error { if err := params.Validate(); err != nil { return err } - ak.paramk.SetParams(ctx, ModuleName, "p", params) - return nil + err := ak.paramk.SetParams(ctx, ModuleName, paramsKey, params) + return err } func (ak AccountKeeper) GetParams(ctx sdk.Context) Params { params := &Params{} - ok, err := ak.paramk.GetParams(ctx, ModuleName, "p", params) + ok, err := ak.paramk.GetParams(ctx, ModuleName, paramsKey, params) if !ok { panic("params key " + ModuleName + " does not exist") diff --git a/tm2/pkg/sdk/auth/test_common.go b/tm2/pkg/sdk/auth/test_common.go index 98450da30f4..586a6170ec6 100644 --- a/tm2/pkg/sdk/auth/test_common.go +++ b/tm2/pkg/sdk/auth/test_common.go @@ -8,6 +8,8 @@ import ( "github.com/gnolang/gno/tm2/pkg/log" "github.com/gnolang/gno/tm2/pkg/sdk" + + "github.com/gnolang/gno/tm2/pkg/sdk/params" "github.com/gnolang/gno/tm2/pkg/std" "github.com/gnolang/gno/tm2/pkg/store" "github.com/gnolang/gno/tm2/pkg/store/iavl" @@ -27,8 +29,10 @@ func setupTestEnv() testEnv { ms := store.NewCommitMultiStore(db) ms.MountStoreWithDB(authCapKey, iavl.StoreConstructor, db) ms.LoadLatestVersion() - - acck := NewAccountKeeper(authCapKey, std.ProtoBaseAccount) + km := params.NewPrefixKeyMapper() + km.RegisterPrefix(ParamsPrefixKey) + paramk := params.NewParamsKeeper(authCapKey, km) + acck := NewAccountKeeper(authCapKey, paramk, std.ProtoBaseAccount) bank := NewDummyBankKeeper(acck) ctx := sdk.NewContext(sdk.RunTxModeDeliver, ms, &bft.Header{Height: 1, ChainID: "test-chain-id"}, log.NewNoopLogger()) diff --git a/tm2/pkg/sdk/bank/common_test.go b/tm2/pkg/sdk/bank/common_test.go index 485134afb00..686abfd5a07 100644 --- a/tm2/pkg/sdk/bank/common_test.go +++ b/tm2/pkg/sdk/bank/common_test.go @@ -32,12 +32,12 @@ func setupTestEnv() testEnv { ms.LoadLatestVersion() ctx := sdk.NewContext(sdk.RunTxModeDeliver, ms, &bft.Header{ChainID: "test-chain-id"}, log.NewNoopLogger()) - acck := auth.NewAccountKeeper( - authCapKey, std.ProtoBaseAccount, - ) km := params.NewPrefixKeyMapper() km.RegisterPrefix(ParamsPrefixKey) paramk := params.NewParamsKeeper(authCapKey, km) + acck := auth.NewAccountKeeper( + authCapKey, paramk, std.ProtoBaseAccount, + ) bank := NewBankKeeper(acck, paramk) return testEnv{ctx: ctx, bank: bank, acck: acck, paramk: paramk} diff --git a/tm2/pkg/sdk/bank/keeper.go b/tm2/pkg/sdk/bank/keeper.go index 3361584deef..e994bdda303 100644 --- a/tm2/pkg/sdk/bank/keeper.go +++ b/tm2/pkg/sdk/bank/keeper.go @@ -69,7 +69,9 @@ func (bank *BankKeeper) AddRestrictedDenoms(ctx sdk.Context, restrictedDenoms .. } if len(bank.params.RestrictedDenoms) == 0 { bank.params.RestrictedDenoms = restrictedDenoms - bank.SetParams(ctx, bank.params) + if err := bank.SetParams(ctx, bank.params); err != nil { + panic(err) + } } bank.updateParams(ctx) } @@ -99,7 +101,9 @@ func (bank *BankKeeper) updateParams(ctx sdk.Context) { params := bank.GetParams(ctx) params.RestrictedDenoms = bank.RestrictedDenoms(ctx) bank.params = params - bank.SetParams(ctx, params) + if err := bank.SetParams(ctx, params); err != nil { + panic(err) + } } // InputOutputCoins handles a list of inputs and outputs @@ -164,11 +168,18 @@ func (bank BankKeeper) canSendCoins(ctx sdk.Context, addr crypto.Address, amt st // SendCoins moves coins from one account to another, restrction could be applied func (bank BankKeeper) SendCoins(ctx sdk.Context, fromAddr crypto.Address, toAddr crypto.Address, amt std.Coins) error { - return bank.sendCoins(ctx, fromAddr, toAddr, amt, true) + // read restricted boolean value from param.IsRestrictedTransfer() + // canSendCoins is true until they have agreed to the waiver + if !bank.canSendCoins(ctx, fromAddr, amt) { + return std.RestrictedTransferError{} + } + + return bank.sendCoins(ctx, fromAddr, toAddr, amt) } +// SendCoinsUnrestricted is used for paying gas. func (bank BankKeeper) SendCoinsUnrestricted(ctx sdk.Context, fromAddr crypto.Address, toAddr crypto.Address, amt std.Coins) error { - return bank.sendCoins(ctx, fromAddr, toAddr, amt, false) + return bank.sendCoins(ctx, fromAddr, toAddr, amt) } func (bank BankKeeper) sendCoins( @@ -176,14 +187,7 @@ func (bank BankKeeper) sendCoins( fromAddr crypto.Address, toAddr crypto.Address, amt std.Coins, - wRestriction bool, // with restriction ) error { - // read restricted boolean value from param.IsRestrictedTransfer() - // canSendCoins is true until they have agreed to the waiver - - if wRestriction && !bank.canSendCoins(ctx, fromAddr, amt) { - return std.RestrictedTransferError{} - } _, err := bank.SubtractCoins(ctx, fromAddr, amt) if err != nil { return err diff --git a/tm2/pkg/sdk/bank/params.go b/tm2/pkg/sdk/bank/params.go index 2a9d12eccc7..2e65ceabcfd 100644 --- a/tm2/pkg/sdk/bank/params.go +++ b/tm2/pkg/sdk/bank/params.go @@ -8,6 +8,8 @@ import ( "github.com/gnolang/gno/tm2/pkg/std" ) +const paramsKey = "p" + type BankParamsContextKey struct{} // Params defines the parameters for the bank module. @@ -53,15 +55,15 @@ func (bank BankKeeper) SetParams(ctx sdk.Context, params Params) error { return err } bank.params = params - bank.paramk.SetParams(ctx, ModuleName, "p", params) + err := bank.paramk.SetParams(ctx, ModuleName, paramsKey, params) - return nil + return err } func (bank BankKeeper) GetParams(ctx sdk.Context) Params { params := &Params{} - ok, err := bank.paramk.GetParams(ctx, ModuleName, "p", params) + ok, err := bank.paramk.GetParams(ctx, ModuleName, paramsKey, params) if !ok { panic("params key " + ModuleName + " does not exist") diff --git a/tm2/pkg/sdk/params/handler.go b/tm2/pkg/sdk/params/handler.go index 893f4563d84..a74cdc36ee0 100644 --- a/tm2/pkg/sdk/params/handler.go +++ b/tm2/pkg/sdk/params/handler.go @@ -29,7 +29,7 @@ func (bh paramsHandler) Process(ctx sdk.Context, msg std.Msg) sdk.Result { func (bh paramsHandler) Query(ctx sdk.Context, req abci.RequestQuery) (res abci.ResponseQuery) { prefix := secondPart(req.Path) - if bh.params.PrefixExist(prefix) { + if bh.params.PrefixExists(prefix) { return bh.queryParam(ctx, req) } res = sdk.ABCIResponseQueryFromError( diff --git a/tm2/pkg/sdk/params/keeper.go b/tm2/pkg/sdk/params/keeper.go index cc02b3b9236..7cdb180374c 100644 --- a/tm2/pkg/sdk/params/keeper.go +++ b/tm2/pkg/sdk/params/keeper.go @@ -40,8 +40,8 @@ var _ ParamsKeeperI = ParamsKeeper{} // global paramstore Keeper. type ParamsKeeper struct { - key store.StoreKey - prefix string + key store.StoreKey + // prefix string prefixKeyMapper PrefixKeyMapper } @@ -162,7 +162,7 @@ func (pk ParamsKeeper) valueStoreKey(prefix string, key string) ([]byte, error) return append([]byte(prefix), []byte(key)...), nil } -func (pk ParamsKeeper) PrefixExist(prefix string) bool { +func (pk ParamsKeeper) PrefixExists(prefix string) bool { _, err := pk.prefixKeyMapper.Map(prefix) if err != nil { return false diff --git a/tm2/pkg/sdk/params/keymapper.go b/tm2/pkg/sdk/params/keymapper.go index 933a8b70798..0e46c1d7f25 100644 --- a/tm2/pkg/sdk/params/keymapper.go +++ b/tm2/pkg/sdk/params/keymapper.go @@ -2,15 +2,7 @@ package params import "fmt" -// KeyMapper is used to map one key string to another. -type KeyMapper interface { - // Map does a transformation on an input key to produce the key - // appropriate for accessing a param keeper's storage instance. - Map(key string) (string, error) -} - -var _ KeyMapper = PrefixKeyMapper{} - +// PrefixKeyMapper is used to map one key string to another. type PrefixKeyMapper struct { keyMap map[string]string } @@ -30,6 +22,8 @@ func (pkm PrefixKeyMapper) IsExist(prefix string) bool { return ok } +// Map does a transformation on an input key to produce the key +// appropriate for accessing a param keeper's storage instance. func (pkm PrefixKeyMapper) Map(prefix string) (string, error) { v, ok := pkm.keyMap[prefix] if !ok { From 599e64a88f518c98c001c7486e303aaf67ac100d Mon Sep 17 00:00:00 2001 From: piux2 <90544084+piux2@users.noreply.github.com> Date: Fri, 27 Dec 2024 02:42:43 -0800 Subject: [PATCH 4/8] fmt --- examples/gno.land/r/nemanya/home/home.gno | 28 +++++++++++------------ 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/examples/gno.land/r/nemanya/home/home.gno b/examples/gno.land/r/nemanya/home/home.gno index 08b831b0d17..08e24baecfd 100644 --- a/examples/gno.land/r/nemanya/home/home.gno +++ b/examples/gno.land/r/nemanya/home/home.gno @@ -27,12 +27,12 @@ type Project struct { } var ( - textArt string - aboutMe string - sponsorInfo string - socialLinks map[string]SocialLink - gnoProjects map[string]Project - otherProjects map[string]Project + textArt string + aboutMe string + sponsorInfo string + socialLinks map[string]SocialLink + gnoProjects map[string]Project + otherProjects map[string]Project totalDonations std.Coins ) @@ -266,15 +266,15 @@ func Withdraw() string { panic(config.ErrUnauthorized) } - banker := std.GetBanker(std.BankerTypeRealmSend) - realmAddress := std.GetOrigPkgAddr() - coins := banker.GetCoins(realmAddress) + banker := std.GetBanker(std.BankerTypeRealmSend) + realmAddress := std.GetOrigPkgAddr() + coins := banker.GetCoins(realmAddress) - if len(coins) == 0 { - return "No coins available to withdraw" - } + if len(coins) == 0 { + return "No coins available to withdraw" + } - banker.SendCoins(realmAddress, config.Address(), coins) + banker.SendCoins(realmAddress, config.Address(), coins) - return "Successfully withdrew all coins to config address" + return "Successfully withdrew all coins to config address" } From b7f317a354283f45befde15104ceeeee09f95be4 Mon Sep 17 00:00:00 2001 From: piux2 <90544084+piux2@users.noreply.github.com> Date: Fri, 27 Dec 2024 10:00:11 -0800 Subject: [PATCH 5/8] fix test --- .../gno.land/r/gov/dao/v2/prop4_filetest.gno | 132 ------------------ tm2/pkg/sdk/params/keeper_test.go | 5 +- tm2/pkg/sdk/params/test_common.go | 3 +- 3 files changed, 5 insertions(+), 135 deletions(-) delete mode 100644 examples/gno.land/r/gov/dao/v2/prop4_filetest.gno diff --git a/examples/gno.land/r/gov/dao/v2/prop4_filetest.gno b/examples/gno.land/r/gov/dao/v2/prop4_filetest.gno deleted file mode 100644 index 13ca572c512..00000000000 --- a/examples/gno.land/r/gov/dao/v2/prop4_filetest.gno +++ /dev/null @@ -1,132 +0,0 @@ -package main - -import ( - "gno.land/p/demo/dao" - "gno.land/r/gov/dao/bridge" - govdaov2 "gno.land/r/gov/dao/v2" - "gno.land/r/sys/params" -) - -func init() { - mExec := params.NewStringPropExecutor("prop1.string", "value1") - title := "Setting prop1.string param" - comment := "setting prop1.string param" - prop := dao.ProposalRequest{ - Title: title, - Description: comment, - Executor: mExec, - } - id := bridge.GovDAO().Propose(prop) - println("new prop", id) -} - -func main() { - println("--") - println(govdaov2.Render("")) - println("--") - println(govdaov2.Render("0")) - println("--") - bridge.GovDAO().VoteOnProposal(0, "YES") - println("--") - println(govdaov2.Render("0")) - println("--") - bridge.GovDAO().ExecuteProposal(0) - println("--") - println(govdaov2.Render("0")) -} - -// Output: -// new prop 0 -// -- -// # GovDAO Proposals -// -// ## [Prop #0 - Setting prop1.string param](/r/gov/dao/v2:0) -// -// **Status: ACTIVE** -// -// **Author: g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm** -// -// -// -- -// # Proposal #0 - Setting prop1.string param -// -// ## Description -// -// setting prop1.string param -// -// ## Proposal information -// -// **Status: ACTIVE** -// -// **Voting stats:** -// - YES 0 (0%) -// - NO 0 (0%) -// - ABSTAIN 0 (0%) -// - MISSING VOTES 10 (100%) -// -// -// **Threshold met: FALSE** -// -// **Author: g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm** -// -// ### Actions -// -// #### [[Vote YES](/r/gov/dao/v2$help&func=VoteOnProposal&id=0&option=YES)] - [[Vote NO](/r/gov/dao/v2$help&func=VoteOnProposal&id=0&option=NO)] - [[Vote ABSTAIN](/r/gov/dao/v2$help&func=VoteOnProposal&id=0&option=ABSTAIN)] -// -// -// -- -// -- -// # Proposal #0 - Setting prop1.string param -// -// ## Description -// -// setting prop1.string param -// -// ## Proposal information -// -// **Status: ACCEPTED** -// -// **Voting stats:** -// - YES 10 (100%) -// - NO 0 (0%) -// - ABSTAIN 0 (0%) -// - MISSING VOTES 0 (0%) -// -// -// **Threshold met: TRUE** -// -// **Author: g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm** -// -// ### Actions -// -// The voting period for this proposal is over. -// -// -// -- -// -- -// # Proposal #0 - Setting prop1.string param -// -// ## Description -// -// setting prop1.string param -// -// ## Proposal information -// -// **Status: EXECUTION SUCCESSFUL** -// -// **Voting stats:** -// - YES 10 (100%) -// - NO 0 (0%) -// - ABSTAIN 0 (0%) -// - MISSING VOTES 0 (0%) -// -// -// **Threshold met: TRUE** -// -// **Author: g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm** -// -// ### Actions -// -// The voting period for this proposal is over. -// -// diff --git a/tm2/pkg/sdk/params/keeper_test.go b/tm2/pkg/sdk/params/keeper_test.go index 3c1c53c5f7e..bbfcfe2ba06 100644 --- a/tm2/pkg/sdk/params/keeper_test.go +++ b/tm2/pkg/sdk/params/keeper_test.go @@ -147,17 +147,18 @@ type Params struct { } func TestGetAndSetParams(t *testing.T) { + prefix := "params_test" env := setupTestEnv() ctx := env.ctx keeper := env.keeper // SetParams a := Params{p1: 1, p2: "a"} - err := keeper.SetParams(ctx, ModuleName, "p", a) + err := keeper.SetParams(ctx, prefix, "p", a) require.NoError(t, err) // GetParams a1 := Params{} - _, err1 := keeper.GetParams(ctx, ModuleName, "p", &a1) + _, err1 := keeper.GetParams(ctx, prefix, "p", &a1) require.NoError(t, err1) require.True(t, amino.DeepEqual(a, a1), "a and a1 should equal") } diff --git a/tm2/pkg/sdk/params/test_common.go b/tm2/pkg/sdk/params/test_common.go index 8e785f89cd5..ef240d86d12 100644 --- a/tm2/pkg/sdk/params/test_common.go +++ b/tm2/pkg/sdk/params/test_common.go @@ -23,8 +23,9 @@ func setupTestEnv() testEnv { ms.MountStoreWithDB(paramsCapKey, iavl.StoreConstructor, db) ms.LoadLatestVersion() + prefix := "params_test" km := NewPrefixKeyMapper() - km.RegisterPrefix(ModuleName) + km.RegisterPrefix(prefix) keeper := NewParamsKeeper(paramsCapKey, km) ctx := sdk.NewContext(sdk.RunTxModeDeliver, ms, &bft.Header{Height: 1, ChainID: "test-chain-id"}, log.NewNoopLogger()) From 41d163383cfa76f57bd2c754adfbdec27601838d Mon Sep 17 00:00:00 2001 From: piux2 <90544084+piux2@users.noreply.github.com> Date: Fri, 27 Dec 2024 10:22:27 -0800 Subject: [PATCH 6/8] lint --- examples/gno.land/r/sys/params/unlock_test.gno | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/gno.land/r/sys/params/unlock_test.gno b/examples/gno.land/r/sys/params/unlock_test.gno index 4926784edd0..36f42836e26 100644 --- a/examples/gno.land/r/sys/params/unlock_test.gno +++ b/examples/gno.land/r/sys/params/unlock_test.gno @@ -14,7 +14,7 @@ func TestProUnlockSend(t *testing.T) { id := ProposeUnlockSend() p, err := govdao.GetPropStore().ProposalByID(id) urequire.NoError(t, err) - urequire.Equal(t, UnlockSendDesc, p.Description()) + urequire.Equal(t, UnlockSendTitle, p.Description()) } func TestFailUnlockSend(t *testing.T) { From 9fd24d5d3c18db17fa3f917e001acee004c95294 Mon Sep 17 00:00:00 2001 From: piux2 <90544084+piux2@users.noreply.github.com> Date: Fri, 27 Dec 2024 11:31:10 -0800 Subject: [PATCH 7/8] fix test --- examples/gno.land/r/sys/params/unlock_test.gno | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/gno.land/r/sys/params/unlock_test.gno b/examples/gno.land/r/sys/params/unlock_test.gno index 36f42836e26..6be6f9f4d6c 100644 --- a/examples/gno.land/r/sys/params/unlock_test.gno +++ b/examples/gno.land/r/sys/params/unlock_test.gno @@ -14,7 +14,7 @@ func TestProUnlockSend(t *testing.T) { id := ProposeUnlockSend() p, err := govdao.GetPropStore().ProposalByID(id) urequire.NoError(t, err) - urequire.Equal(t, UnlockSendTitle, p.Description()) + urequire.Equal(t, UnlockSendTitle, p.Title()) } func TestFailUnlockSend(t *testing.T) { From 9f05d035d4f014715287aa3888c609490ef0164f Mon Sep 17 00:00:00 2001 From: piux2 <90544084+piux2@users.noreply.github.com> Date: Fri, 27 Dec 2024 11:33:44 -0800 Subject: [PATCH 8/8] gno tidy --- examples/gno.land/r/nemanya/config/gno.mod | 2 +- examples/gno.land/r/nemanya/home/gno.mod | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/gno.land/r/nemanya/config/gno.mod b/examples/gno.land/r/nemanya/config/gno.mod index 8ffbc32f571..4388b5bd525 100644 --- a/examples/gno.land/r/nemanya/config/gno.mod +++ b/examples/gno.land/r/nemanya/config/gno.mod @@ -1 +1 @@ -module gno.land/r/nemanya/config \ No newline at end of file +module gno.land/r/nemanya/config diff --git a/examples/gno.land/r/nemanya/home/gno.mod b/examples/gno.land/r/nemanya/home/gno.mod index 1994cf7c11b..d0220197489 100644 --- a/examples/gno.land/r/nemanya/home/gno.mod +++ b/examples/gno.land/r/nemanya/home/gno.mod @@ -1 +1 @@ -module gno.land/r/nemanya/home \ No newline at end of file +module gno.land/r/nemanya/home