From ce579af98bd04fc129be0c7eea45f6edaddf8ec9 Mon Sep 17 00:00:00 2001 From: deelawn Date: Mon, 5 Aug 2024 15:54:13 -0700 Subject: [PATCH 01/15] locked accounts restricted sending gnot --- gno.land/cmd/gnoland/genesis_generate.go | 12 +++++ gno.land/pkg/gnoland/app.go | 3 +- tm2/pkg/bft/types/genesis.go | 13 ++--- tm2/pkg/sdk/auth/ante.go | 4 +- tm2/pkg/sdk/auth/types.go | 1 + tm2/pkg/sdk/bank/keeper.go | 69 ++++++++++++++++++++++-- tm2/pkg/sdk/bank/keeper_test.go | 38 +++++++++++++ tm2/pkg/std/account.go | 10 ++++ tm2/pkg/std/coin.go | 10 ++++ 9 files changed, 148 insertions(+), 12 deletions(-) diff --git a/gno.land/cmd/gnoland/genesis_generate.go b/gno.land/cmd/gnoland/genesis_generate.go index 751ac14ae62..53b790a0e86 100644 --- a/gno.land/cmd/gnoland/genesis_generate.go +++ b/gno.land/cmd/gnoland/genesis_generate.go @@ -20,6 +20,7 @@ type generateCfg struct { blockMaxDataBytes int64 blockMaxGas int64 blockTimeIota int64 + gnotUnrestricted bool } // newGenerateCmd creates the genesis generate subcommand @@ -89,6 +90,13 @@ func (c *generateCfg) RegisterFlags(fs *flag.FlagSet) { types.BlockTimeIotaMS, "the block time iota (in ms)", ) + + fs.BoolVar( + &c.gnotUnrestricted, + "gnot-unrestricted", + false, + "allow sending of GNOT from locked accounts", + ) } func execGenerate(cfg *generateCfg, io commands.IO) error { @@ -125,6 +133,10 @@ func execGenerate(cfg *generateCfg, io commands.IO) error { genesis.ConsensusParams.Block.TimeIotaMS = cfg.blockTimeIota } + if cfg.gnotUnrestricted { + genesis.RestrictedTokens = []string{"ugnot"} + } + // Validate the genesis if validateErr := genesis.ValidateAndComplete(); validateErr != nil { return fmt.Errorf("unable to validate genesis, %w", validateErr) diff --git a/gno.land/pkg/gnoland/app.go b/gno.land/pkg/gnoland/app.go index f4d353411f8..82fb93ac343 100644 --- a/gno.land/pkg/gnoland/app.go +++ b/gno.land/pkg/gnoland/app.go @@ -87,7 +87,8 @@ func NewAppWithOptions(cfg *AppOptions) (abci.Application, error) { // Construct keepers. acctKpr := auth.NewAccountKeeper(mainKey, ProtoGnoAccount) - bankKpr := bank.NewBankKeeper(acctKpr) + // TODO: set restricted denoms from genesis. + bankKpr := bank.NewBankKeeperWithRestrictedDenoms(acctKpr, "ugnot") // XXX: Embed this ? stdlibsDir := filepath.Join(cfg.GnoRootDir, "gnovm", "stdlibs") diff --git a/tm2/pkg/bft/types/genesis.go b/tm2/pkg/bft/types/genesis.go index c03f7acc09e..eecb1c858a2 100644 --- a/tm2/pkg/bft/types/genesis.go +++ b/tm2/pkg/bft/types/genesis.go @@ -44,12 +44,13 @@ type GenesisValidator struct { // GenesisDoc defines the initial conditions for a tendermint blockchain, in particular its validator set. type GenesisDoc struct { - GenesisTime time.Time `json:"genesis_time"` - ChainID string `json:"chain_id"` - ConsensusParams abci.ConsensusParams `json:"consensus_params,omitempty"` - Validators []GenesisValidator `json:"validators,omitempty"` - AppHash []byte `json:"app_hash"` - AppState interface{} `json:"app_state,omitempty"` + GenesisTime time.Time `json:"genesis_time"` + ChainID string `json:"chain_id"` + ConsensusParams abci.ConsensusParams `json:"consensus_params,omitempty"` + Validators []GenesisValidator `json:"validators,omitempty"` + RestrictedTokens []string `json:"restricted_tokens,omitempty"` + AppHash []byte `json:"app_hash"` + AppState interface{} `json:"app_state,omitempty"` } // SaveAs is a utility method for saving GenensisDoc as a JSON file. diff --git a/tm2/pkg/sdk/auth/ante.go b/tm2/pkg/sdk/auth/ante.go index 49662b47a55..339e6e950e5 100644 --- a/tm2/pkg/sdk/auth/ante.go +++ b/tm2/pkg/sdk/auth/ante.go @@ -363,7 +363,9 @@ 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, meaning locked accounts can only + // send restricted coins to pay for gas. + err := bank.SendCoinsUnrestricted(ctx, acc.GetAddress(), FeeCollectorAddress(), fees) if err != nil { return abciResult(err) } diff --git a/tm2/pkg/sdk/auth/types.go b/tm2/pkg/sdk/auth/types.go index 8bbc5e39e3b..ce89e55b44d 100644 --- a/tm2/pkg/sdk/auth/types.go +++ b/tm2/pkg/sdk/auth/types.go @@ -20,4 +20,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/keeper.go b/tm2/pkg/sdk/bank/keeper.go index 5d3699c99ef..342a314f099 100644 --- a/tm2/pkg/sdk/bank/keeper.go +++ b/tm2/pkg/sdk/bank/keeper.go @@ -30,17 +30,30 @@ var _ BankKeeperI = BankKeeper{} type BankKeeper struct { ViewKeeper - acck auth.AccountKeeper + acck auth.AccountKeeper + restrictedDenoms map[string]struct{} } // NewBankKeeper returns a new BankKeeper. func NewBankKeeper(acck auth.AccountKeeper) BankKeeper { return BankKeeper{ - ViewKeeper: NewViewKeeper(acck), - acck: acck, + ViewKeeper: NewViewKeeper(acck), + acck: acck, + restrictedDenoms: map[string]struct{}{}, } } +func NewBankKeeperWithRestrictedDenoms(acck auth.AccountKeeper, restrictedDenoms ...string) BankKeeper { + restrictedDenomsStore := make(map[string]struct{}, len(restrictedDenoms)) + for _, denom := range restrictedDenoms { + restrictedDenomsStore[denom] = struct{}{} + } + + bankKeeper := NewBankKeeper(acck) + bankKeeper.restrictedDenoms = restrictedDenomsStore + return bankKeeper +} + // InputOutputCoins handles a list of inputs and outputs func (bank BankKeeper) InputOutputCoins(ctx sdk.Context, inputs []Input, outputs []Output) error { // Safety check ensuring that when sending coins the bank must maintain the @@ -50,6 +63,10 @@ func (bank BankKeeper) InputOutputCoins(ctx sdk.Context, inputs []Input, outputs } for _, in := range inputs { + if !bank.canSendCoins(ctx, in.Address, in.Coins) { + return std.ErrLockedAccount + } + _, err := bank.SubtractCoins(ctx, in.Address, in.Coins) if err != nil { return err @@ -84,8 +101,52 @@ func (bank BankKeeper) InputOutputCoins(ctx sdk.Context, inputs []Input, outputs return nil } +// 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 { + // Coins of a restricted denomination cannot be transferred unless the account is unlocked. + if amt.ContainOneOfDenom(bank.restrictedDenoms) { + if acc := bank.acck.GetAccount(ctx, addr); acc != nil && acc.IsLocked() { + return false + } + } + + return true +} + +// SendCoinsUnrestricted moves coins from one account to another, regardless of any restrictions. +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, +) error { + return bank.sendCoins(ctx, fromAddr, toAddr, amt, true) +} + // SendCoins moves coins from one account to another -func (bank BankKeeper) SendCoins(ctx sdk.Context, fromAddr crypto.Address, toAddr crypto.Address, amt std.Coins) error { +func (bank BankKeeper) sendCoins( + ctx sdk.Context, + fromAddr crypto.Address, + toAddr crypto.Address, + amt std.Coins, + restricted bool, +) error { + // TODO: updated this comment after the waiver tx has been added. + // Locked accounts are restricted from transferrring GNOT until they have agreed to the waiver. + + if restricted && !bank.canSendCoins(ctx, fromAddr, amt) { + return std.ErrLockedAccount + } + _, 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 59b4c12689c..fdf77fab8eb 100644 --- a/tm2/pkg/sdk/bank/keeper_test.go +++ b/tm2/pkg/sdk/bank/keeper_test.go @@ -137,6 +137,44 @@ func TestBankKeeper(t *testing.T) { require.Error(t, err) } +func TestBankKeeperWithRestrictions(t *testing.T) { + env := setupTestEnv() + ctx := env.ctx + + bankKeeper := NewBankKeeperWithRestrictedDenoms(env.acck, "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.ErrLockedAccount, "expected locked account 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)))) +} + func TestViewKeeper(t *testing.T) { t.Parallel() diff --git a/tm2/pkg/std/account.go b/tm2/pkg/std/account.go index c70f43d22e9..b3a6018aa23 100644 --- a/tm2/pkg/std/account.go +++ b/tm2/pkg/std/account.go @@ -12,6 +12,8 @@ import ( _ "github.com/gnolang/gno/tm2/pkg/crypto/secp256k1" ) +var ErrLockedAccount = errors.New("account is locked") + // Account is an interface used to store coins at a given address within state. // It presumes a notion of sequence numbers for replay protection, a notion of // account numbers for replay protection for previously pruned accounts, and a @@ -34,6 +36,8 @@ type Account interface { GetCoins() Coins SetCoins(Coins) error + IsLocked() bool + // Ensure that account implements stringer String() string } @@ -49,6 +53,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"` + IsUnlocked bool `json:"is_unlocked" yaml:"is_unlocked"` } // NewBaseAccount creates a new BaseAccount object @@ -151,3 +156,8 @@ func (acc *BaseAccount) SetSequence(seq uint64) error { acc.Sequence = seq return nil } + +// IsLocked returns true if the account is not marked as unlocked -- Implements Account. +func (acc BaseAccount) IsLocked() bool { + return !acc.IsUnlocked +} diff --git a/tm2/pkg/std/coin.go b/tm2/pkg/std/coin.go index 4f36757efc0..af028c81f07 100644 --- a/tm2/pkg/std/coin.go +++ b/tm2/pkg/std/coin.go @@ -222,6 +222,16 @@ func (coins Coins) String() string { return out[:len(out)-1] } +func (coins Coins) ContainOneOfDenom(denoms map[string]struct{}) bool { + for _, coin := range coins { + if _, ok := denoms[coin.Denom]; ok { + return true + } + } + + return false +} + // IsValid asserts the Coins are sorted, have positive amount, // and Denom does not contain upper case characters. func (coins Coins) IsValid() bool { From af7fbe286adbb1e907e994221b0fbc9112398870 Mon Sep 17 00:00:00 2001 From: deelawn Date: Mon, 5 Aug 2024 16:34:16 -0700 Subject: [PATCH 02/15] inject restricted denoms from genesis into banker --- gno.land/cmd/gnoland/start.go | 7 ++++++- gno.land/pkg/gnoland/app.go | 29 +++++++++++++++++++++++------ tm2/pkg/bft/node/node.go | 4 ++++ tm2/pkg/sdk/auth/test_common.go | 4 ++++ tm2/pkg/sdk/bank/keeper.go | 8 ++++---- 5 files changed, 41 insertions(+), 11 deletions(-) diff --git a/gno.land/cmd/gnoland/start.go b/gno.land/cmd/gnoland/start.go index 21f0cb4b1a6..8149cce2932 100644 --- a/gno.land/cmd/gnoland/start.go +++ b/gno.land/cmd/gnoland/start.go @@ -244,17 +244,22 @@ func execStart(ctx context.Context, c *startCfg, io commands.IO) error { evsw := events.NewEventSwitch() // Create application and node - cfg.LocalApp, err = gnoland.NewApp(nodeDir, c.skipFailingGenesisTxs, evsw, logger) + localApp, err := gnoland.NewApp(nodeDir, c.skipFailingGenesisTxs, evsw, logger) if err != nil { return fmt.Errorf("unable to create the Gnoland app, %w", err) } + cfg.LocalApp = localApp + // Create a default node, with the given setup gnoNode, err := node.DefaultNewNode(cfg, genesisPath, evsw, logger) if err != nil { return fmt.Errorf("unable to create the Gnoland node, %w", err) } + // Apply restricted denoms from genesis to the banker. + localApp.DefineDenoms(gnoNode.GenesisDoc().RestrictedTokens...) + // Start the node (async) if err := gnoNode.Start(); err != nil { return fmt.Errorf("unable to start the Gnoland node, %w", err) diff --git a/gno.land/pkg/gnoland/app.go b/gno.land/pkg/gnoland/app.go index 82fb93ac343..542835505af 100644 --- a/gno.land/pkg/gnoland/app.go +++ b/gno.land/pkg/gnoland/app.go @@ -28,6 +28,21 @@ import ( "github.com/gnolang/gno/tm2/pkg/db/memdb" ) +type App struct { + *sdk.BaseApp + restrictedDenomsDefiner +} + +type restrictedDenomsDefiner struct { + target map[string]struct{} +} + +func (d restrictedDenomsDefiner) DefineDenoms(denoms ...string) { + for _, denom := range denoms { + d.target[denom] = struct{}{} + } +} + type AppOptions struct { DB dbm.DB // `gnoRootDir` should point to the local location of the gno repository. @@ -67,7 +82,7 @@ func (c *AppOptions) validate() error { } // NewAppWithOptions creates the GnoLand application with specified options -func NewAppWithOptions(cfg *AppOptions) (abci.Application, error) { +func NewAppWithOptions(cfg *AppOptions) (*App, error) { if err := cfg.validate(); err != nil { return nil, err } @@ -78,7 +93,8 @@ func NewAppWithOptions(cfg *AppOptions) (abci.Application, error) { // Create BaseApp. // TODO: Add a consensus based min gas prices for the node, by default it does not check - baseApp := sdk.NewBaseApp("gnoland", cfg.Logger, cfg.DB, baseKey, mainKey) + var baseApp App + baseApp.BaseApp = sdk.NewBaseApp("gnoland", cfg.Logger, cfg.DB, baseKey, mainKey) baseApp.SetAppVersion("dev") // Set mounts for BaseApp's MultiStore. @@ -88,14 +104,15 @@ func NewAppWithOptions(cfg *AppOptions) (abci.Application, error) { // Construct keepers. acctKpr := auth.NewAccountKeeper(mainKey, ProtoGnoAccount) // TODO: set restricted denoms from genesis. - bankKpr := bank.NewBankKeeperWithRestrictedDenoms(acctKpr, "ugnot") + bankKpr := bank.NewBankKeeper(acctKpr) + baseApp.restrictedDenomsDefiner = restrictedDenomsDefiner{bankKpr.RestrictedDenoms} // XXX: Embed this ? stdlibsDir := filepath.Join(cfg.GnoRootDir, "gnovm", "stdlibs") vmk := vm.NewVMKeeper(baseKey, mainKey, acctKpr, bankKpr, stdlibsDir, cfg.MaxCycles) // Set InitChainer - baseApp.SetInitChainer(InitChainer(baseApp, acctKpr, bankKpr, cfg.GenesisTxHandler)) + baseApp.SetInitChainer(InitChainer(baseApp.BaseApp, acctKpr, bankKpr, cfg.GenesisTxHandler)) // Set AnteHandler authOptions := auth.AnteOptions{ @@ -147,7 +164,7 @@ func NewAppWithOptions(cfg *AppOptions) (abci.Application, error) { vmk.Initialize(cfg.Logger, ms, cfg.CacheStdlibLoad) ms.MultiWrite() // XXX why was't this needed? - return baseApp, nil + return &baseApp, nil } // NewApp creates the GnoLand application. @@ -156,7 +173,7 @@ func NewApp( skipFailingGenesisTxs bool, evsw events.EventSwitch, logger *slog.Logger, -) (abci.Application, error) { +) (*App, error) { var err error cfg := NewAppOptions() diff --git a/tm2/pkg/bft/node/node.go b/tm2/pkg/bft/node/node.go index e29de3dd1ae..229f546beec 100644 --- a/tm2/pkg/bft/node/node.go +++ b/tm2/pkg/bft/node/node.go @@ -177,6 +177,10 @@ type Node struct { firstBlockSignal <-chan struct{} } +func (n *Node) GenensisDoc() types.GenesisDoc { + return *n.genesisDoc +} + func initDBs(config *cfg.Config, dbProvider DBProvider) (blockStore *store.BlockStore, stateDB dbm.DB, err error) { var blockStoreDB dbm.DB blockStoreDB, err = dbProvider(&DBContext{"blockstore", config}) diff --git a/tm2/pkg/sdk/auth/test_common.go b/tm2/pkg/sdk/auth/test_common.go index f833a0b0564..b86bb754c4b 100644 --- a/tm2/pkg/sdk/auth/test_common.go +++ b/tm2/pkg/sdk/auth/test_common.go @@ -84,3 +84,7 @@ func (bank DummyBankKeeper) SendCoins(ctx sdk.Context, fromAddr crypto.Address, return nil } + +func (bank DummyBankKeeper) SendCoinsUnrestricted(ctx sdk.Context, fromAddr crypto.Address, toAddr crypto.Address, amt std.Coins) error { + return bank.SendCoins(ctx, fromAddr, toAddr, amt) +} diff --git a/tm2/pkg/sdk/bank/keeper.go b/tm2/pkg/sdk/bank/keeper.go index 342a314f099..72defa2f1be 100644 --- a/tm2/pkg/sdk/bank/keeper.go +++ b/tm2/pkg/sdk/bank/keeper.go @@ -31,7 +31,7 @@ type BankKeeper struct { ViewKeeper acck auth.AccountKeeper - restrictedDenoms map[string]struct{} + RestrictedDenoms map[string]struct{} } // NewBankKeeper returns a new BankKeeper. @@ -39,7 +39,7 @@ func NewBankKeeper(acck auth.AccountKeeper) BankKeeper { return BankKeeper{ ViewKeeper: NewViewKeeper(acck), acck: acck, - restrictedDenoms: map[string]struct{}{}, + RestrictedDenoms: map[string]struct{}{}, } } @@ -50,7 +50,7 @@ func NewBankKeeperWithRestrictedDenoms(acck auth.AccountKeeper, restrictedDenoms } bankKeeper := NewBankKeeper(acck) - bankKeeper.restrictedDenoms = restrictedDenomsStore + bankKeeper.RestrictedDenoms = restrictedDenomsStore return bankKeeper } @@ -104,7 +104,7 @@ func (bank BankKeeper) InputOutputCoins(ctx sdk.Context, inputs []Input, outputs // 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 { // Coins of a restricted denomination cannot be transferred unless the account is unlocked. - if amt.ContainOneOfDenom(bank.restrictedDenoms) { + if amt.ContainOneOfDenom(bank.RestrictedDenoms) { if acc := bank.acck.GetAccount(ctx, addr); acc != nil && acc.IsLocked() { return false } From 065dddd2f2d76d55368d21135fc86373adc119fd Mon Sep 17 00:00:00 2001 From: deelawn Date: Thu, 8 Aug 2024 12:06:28 -0700 Subject: [PATCH 03/15] change now denoms are injected into the banker --- gno.land/cmd/gnoland/genesis_generate.go | 12 +++--- gno.land/cmd/gnoland/start.go | 7 +--- gno.land/pkg/gnoland/app.go | 50 ++++++++---------------- tm2/pkg/bft/abci/types/types.go | 11 +++--- tm2/pkg/bft/consensus/replay.go | 11 +++--- tm2/pkg/bft/types/genesis.go | 2 +- tm2/pkg/sdk/bank/keeper.go | 16 ++++++-- 7 files changed, 49 insertions(+), 60 deletions(-) diff --git a/gno.land/cmd/gnoland/genesis_generate.go b/gno.land/cmd/gnoland/genesis_generate.go index 53b790a0e86..388ea25348d 100644 --- a/gno.land/cmd/gnoland/genesis_generate.go +++ b/gno.land/cmd/gnoland/genesis_generate.go @@ -20,7 +20,7 @@ type generateCfg struct { blockMaxDataBytes int64 blockMaxGas int64 blockTimeIota int64 - gnotUnrestricted bool + ugnotUnrestricted bool } // newGenerateCmd creates the genesis generate subcommand @@ -92,10 +92,10 @@ func (c *generateCfg) RegisterFlags(fs *flag.FlagSet) { ) fs.BoolVar( - &c.gnotUnrestricted, - "gnot-unrestricted", + &c.ugnotUnrestricted, + "ugnot-unrestricted", false, - "allow sending of GNOT from locked accounts", + "allow sending of ugnot from locked accounts", ) } @@ -133,8 +133,8 @@ func execGenerate(cfg *generateCfg, io commands.IO) error { genesis.ConsensusParams.Block.TimeIotaMS = cfg.blockTimeIota } - if cfg.gnotUnrestricted { - genesis.RestrictedTokens = []string{"ugnot"} + if cfg.ugnotUnrestricted { + genesis.RestrictedDenoms = []string{"ugnot"} } // Validate the genesis diff --git a/gno.land/cmd/gnoland/start.go b/gno.land/cmd/gnoland/start.go index 8149cce2932..21f0cb4b1a6 100644 --- a/gno.land/cmd/gnoland/start.go +++ b/gno.land/cmd/gnoland/start.go @@ -244,22 +244,17 @@ func execStart(ctx context.Context, c *startCfg, io commands.IO) error { evsw := events.NewEventSwitch() // Create application and node - localApp, err := gnoland.NewApp(nodeDir, c.skipFailingGenesisTxs, evsw, logger) + cfg.LocalApp, err = gnoland.NewApp(nodeDir, c.skipFailingGenesisTxs, evsw, logger) if err != nil { return fmt.Errorf("unable to create the Gnoland app, %w", err) } - cfg.LocalApp = localApp - // Create a default node, with the given setup gnoNode, err := node.DefaultNewNode(cfg, genesisPath, evsw, logger) if err != nil { return fmt.Errorf("unable to create the Gnoland node, %w", err) } - // Apply restricted denoms from genesis to the banker. - localApp.DefineDenoms(gnoNode.GenesisDoc().RestrictedTokens...) - // Start the node (async) if err := gnoNode.Start(); err != nil { return fmt.Errorf("unable to start the Gnoland node, %w", err) diff --git a/gno.land/pkg/gnoland/app.go b/gno.land/pkg/gnoland/app.go index 542835505af..a069c951fe3 100644 --- a/gno.land/pkg/gnoland/app.go +++ b/gno.land/pkg/gnoland/app.go @@ -28,21 +28,6 @@ import ( "github.com/gnolang/gno/tm2/pkg/db/memdb" ) -type App struct { - *sdk.BaseApp - restrictedDenomsDefiner -} - -type restrictedDenomsDefiner struct { - target map[string]struct{} -} - -func (d restrictedDenomsDefiner) DefineDenoms(denoms ...string) { - for _, denom := range denoms { - d.target[denom] = struct{}{} - } -} - type AppOptions struct { DB dbm.DB // `gnoRootDir` should point to the local location of the gno repository. @@ -82,7 +67,7 @@ func (c *AppOptions) validate() error { } // NewAppWithOptions creates the GnoLand application with specified options -func NewAppWithOptions(cfg *AppOptions) (*App, error) { +func NewAppWithOptions(cfg *AppOptions) (abci.Application, error) { if err := cfg.validate(); err != nil { return nil, err } @@ -93,26 +78,24 @@ func NewAppWithOptions(cfg *AppOptions) (*App, error) { // Create BaseApp. // TODO: Add a consensus based min gas prices for the node, by default it does not check - var baseApp App - baseApp.BaseApp = sdk.NewBaseApp("gnoland", cfg.Logger, cfg.DB, baseKey, mainKey) - baseApp.SetAppVersion("dev") + app := sdk.NewBaseApp("gnoland", cfg.Logger, cfg.DB, baseKey, mainKey) + app.SetAppVersion("dev") // Set mounts for BaseApp's MultiStore. - baseApp.MountStoreWithDB(mainKey, iavl.StoreConstructor, cfg.DB) - baseApp.MountStoreWithDB(baseKey, dbadapter.StoreConstructor, cfg.DB) + app.MountStoreWithDB(mainKey, iavl.StoreConstructor, cfg.DB) + app.MountStoreWithDB(baseKey, dbadapter.StoreConstructor, cfg.DB) // Construct keepers. acctKpr := auth.NewAccountKeeper(mainKey, ProtoGnoAccount) // TODO: set restricted denoms from genesis. bankKpr := bank.NewBankKeeper(acctKpr) - baseApp.restrictedDenomsDefiner = restrictedDenomsDefiner{bankKpr.RestrictedDenoms} // XXX: Embed this ? stdlibsDir := filepath.Join(cfg.GnoRootDir, "gnovm", "stdlibs") vmk := vm.NewVMKeeper(baseKey, mainKey, acctKpr, bankKpr, stdlibsDir, cfg.MaxCycles) // Set InitChainer - baseApp.SetInitChainer(InitChainer(baseApp.BaseApp, acctKpr, bankKpr, cfg.GenesisTxHandler)) + app.SetInitChainer(InitChainer(app, acctKpr, bankKpr, cfg.GenesisTxHandler)) // Set AnteHandler authOptions := auth.AnteOptions{ @@ -120,7 +103,7 @@ func NewAppWithOptions(cfg *AppOptions) (*App, error) { } authAnteHandler := auth.NewAnteHandler( acctKpr, bankKpr, auth.DefaultSigVerificationGasConsumer, authOptions) - baseApp.SetAnteHandler( + app.SetAnteHandler( // Override default AnteHandler with custom logic. func(ctx sdk.Context, tx std.Tx, simulate bool) ( newCtx sdk.Context, res sdk.Result, abort bool, @@ -141,30 +124,30 @@ func NewAppWithOptions(cfg *AppOptions) (*App, error) { ) // Set EndBlocker - baseApp.SetEndBlocker( + app.SetEndBlocker( EndBlocker( c, vmk, - baseApp, + app, ), ) // Set a handler Route. - baseApp.Router().AddRoute("auth", auth.NewHandler(acctKpr)) - baseApp.Router().AddRoute("bank", bank.NewHandler(bankKpr)) - baseApp.Router().AddRoute("vm", vm.NewHandler(vmk)) + app.Router().AddRoute("auth", auth.NewHandler(acctKpr)) + app.Router().AddRoute("bank", bank.NewHandler(bankKpr)) + app.Router().AddRoute("vm", vm.NewHandler(vmk)) // Load latest version. - if err := baseApp.LoadLatestVersion(); err != nil { + if err := app.LoadLatestVersion(); err != nil { return nil, err } // Initialize the VMKeeper. - ms := baseApp.GetCacheMultiStore() + ms := app.GetCacheMultiStore() vmk.Initialize(cfg.Logger, ms, cfg.CacheStdlibLoad) ms.MultiWrite() // XXX why was't this needed? - return &baseApp, nil + return app, nil } // NewApp creates the GnoLand application. @@ -173,7 +156,7 @@ func NewApp( skipFailingGenesisTxs bool, evsw events.EventSwitch, logger *slog.Logger, -) (*App, error) { +) (abci.Application, error) { var err error cfg := NewAppOptions() @@ -216,6 +199,7 @@ func InitChainer( if req.AppState != nil { // Get genesis state genState := req.AppState.(GnoGenesisState) + bankKpr.InitRestrictedDenoms(req.RestrictedDenoms...) // Parse and set genesis state balances for _, bal := range genState.Balances { diff --git a/tm2/pkg/bft/abci/types/types.go b/tm2/pkg/bft/abci/types/types.go index 42376e712a6..19a4f1e3bc4 100644 --- a/tm2/pkg/bft/abci/types/types.go +++ b/tm2/pkg/bft/abci/types/types.go @@ -41,11 +41,12 @@ type RequestSetOption struct { type RequestInitChain struct { RequestBase - Time time.Time - ChainID string - ConsensusParams *ConsensusParams - Validators []ValidatorUpdate - AppState interface{} + Time time.Time + ChainID string + ConsensusParams *ConsensusParams + Validators []ValidatorUpdate + RestrictedDenoms []string + AppState interface{} } type RequestQuery struct { diff --git a/tm2/pkg/bft/consensus/replay.go b/tm2/pkg/bft/consensus/replay.go index 02e6dade72c..f9607cc2474 100644 --- a/tm2/pkg/bft/consensus/replay.go +++ b/tm2/pkg/bft/consensus/replay.go @@ -296,11 +296,12 @@ func (h *Handshaker) ReplayBlocks( nextVals := validatorSet.ABCIValidatorUpdates() csParams := h.genDoc.ConsensusParams req := abci.RequestInitChain{ - Time: h.genDoc.GenesisTime, - ChainID: h.genDoc.ChainID, - ConsensusParams: &csParams, - Validators: nextVals, - AppState: h.genDoc.AppState, + Time: h.genDoc.GenesisTime, + ChainID: h.genDoc.ChainID, + ConsensusParams: &csParams, + Validators: nextVals, + AppState: h.genDoc.AppState, + RestrictedDenoms: h.genDoc.RestrictedDenoms, } res, err := proxyApp.Consensus().InitChainSync(req) if err != nil { diff --git a/tm2/pkg/bft/types/genesis.go b/tm2/pkg/bft/types/genesis.go index eecb1c858a2..811830fb43e 100644 --- a/tm2/pkg/bft/types/genesis.go +++ b/tm2/pkg/bft/types/genesis.go @@ -48,7 +48,7 @@ type GenesisDoc struct { ChainID string `json:"chain_id"` ConsensusParams abci.ConsensusParams `json:"consensus_params,omitempty"` Validators []GenesisValidator `json:"validators,omitempty"` - RestrictedTokens []string `json:"restricted_tokens,omitempty"` + RestrictedDenoms []string `json:"restricted_denoms,omitempty"` AppHash []byte `json:"app_hash"` AppState interface{} `json:"app_state,omitempty"` } diff --git a/tm2/pkg/sdk/bank/keeper.go b/tm2/pkg/sdk/bank/keeper.go index 72defa2f1be..448dcf8cf07 100644 --- a/tm2/pkg/sdk/bank/keeper.go +++ b/tm2/pkg/sdk/bank/keeper.go @@ -21,6 +21,8 @@ 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 + + InitRestrictedDenoms(restrictedDenoms ...string) } var _ BankKeeperI = BankKeeper{} @@ -31,7 +33,7 @@ type BankKeeper struct { ViewKeeper acck auth.AccountKeeper - RestrictedDenoms map[string]struct{} + restrictedDenoms map[string]struct{} } // NewBankKeeper returns a new BankKeeper. @@ -39,7 +41,7 @@ func NewBankKeeper(acck auth.AccountKeeper) BankKeeper { return BankKeeper{ ViewKeeper: NewViewKeeper(acck), acck: acck, - RestrictedDenoms: map[string]struct{}{}, + restrictedDenoms: map[string]struct{}{}, } } @@ -50,10 +52,16 @@ func NewBankKeeperWithRestrictedDenoms(acck auth.AccountKeeper, restrictedDenoms } bankKeeper := NewBankKeeper(acck) - bankKeeper.RestrictedDenoms = restrictedDenomsStore + bankKeeper.restrictedDenoms = restrictedDenomsStore return bankKeeper } +func (bank BankKeeper) InitRestrictedDenoms(restrictedDenoms ...string) { + for _, denom := range restrictedDenoms { + bank.restrictedDenoms[denom] = struct{}{} + } +} + // InputOutputCoins handles a list of inputs and outputs func (bank BankKeeper) InputOutputCoins(ctx sdk.Context, inputs []Input, outputs []Output) error { // Safety check ensuring that when sending coins the bank must maintain the @@ -104,7 +112,7 @@ func (bank BankKeeper) InputOutputCoins(ctx sdk.Context, inputs []Input, outputs // 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 { // Coins of a restricted denomination cannot be transferred unless the account is unlocked. - if amt.ContainOneOfDenom(bank.RestrictedDenoms) { + if amt.ContainOneOfDenom(bank.restrictedDenoms) { if acc := bank.acck.GetAccount(ctx, addr); acc != nil && acc.IsLocked() { return false } From 9d69ba83dcfdd2fda0265437480eb20f9f48facf Mon Sep 17 00:00:00 2001 From: deelawn Date: Thu, 8 Aug 2024 14:01:56 -0700 Subject: [PATCH 04/15] comment typo --- tm2/pkg/sdk/bank/keeper.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tm2/pkg/sdk/bank/keeper.go b/tm2/pkg/sdk/bank/keeper.go index 448dcf8cf07..c28cd060660 100644 --- a/tm2/pkg/sdk/bank/keeper.go +++ b/tm2/pkg/sdk/bank/keeper.go @@ -149,7 +149,7 @@ func (bank BankKeeper) sendCoins( restricted bool, ) error { // TODO: updated this comment after the waiver tx has been added. - // Locked accounts are restricted from transferrring GNOT until they have agreed to the waiver. + // Locked accounts are restricted from transferring GNOT until they have agreed to the waiver. if restricted && !bank.canSendCoins(ctx, fromAddr, amt) { return std.ErrLockedAccount From 1a94f931c0d978f7c27c90326b6bfd9351ce771e Mon Sep 17 00:00:00 2001 From: deelawn Date: Thu, 8 Aug 2024 15:52:29 -0700 Subject: [PATCH 05/15] fix boolean check --- gno.land/cmd/gnoland/genesis_generate.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gno.land/cmd/gnoland/genesis_generate.go b/gno.land/cmd/gnoland/genesis_generate.go index 388ea25348d..43f4ed833c6 100644 --- a/gno.land/cmd/gnoland/genesis_generate.go +++ b/gno.land/cmd/gnoland/genesis_generate.go @@ -133,7 +133,7 @@ func execGenerate(cfg *generateCfg, io commands.IO) error { genesis.ConsensusParams.Block.TimeIotaMS = cfg.blockTimeIota } - if cfg.ugnotUnrestricted { + if !cfg.ugnotUnrestricted { genesis.RestrictedDenoms = []string{"ugnot"} } From 2e3176a7a88687220f9a22f639877b7231f6f6a8 Mon Sep 17 00:00:00 2001 From: deelawn Date: Thu, 8 Aug 2024 15:52:55 -0700 Subject: [PATCH 06/15] move bank restricted denom init --- gno.land/pkg/gnoland/app.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gno.land/pkg/gnoland/app.go b/gno.land/pkg/gnoland/app.go index a069c951fe3..98128357bae 100644 --- a/gno.land/pkg/gnoland/app.go +++ b/gno.land/pkg/gnoland/app.go @@ -87,7 +87,6 @@ func NewAppWithOptions(cfg *AppOptions) (abci.Application, error) { // Construct keepers. acctKpr := auth.NewAccountKeeper(mainKey, ProtoGnoAccount) - // TODO: set restricted denoms from genesis. bankKpr := bank.NewBankKeeper(acctKpr) // XXX: Embed this ? @@ -199,7 +198,6 @@ func InitChainer( if req.AppState != nil { // Get genesis state genState := req.AppState.(GnoGenesisState) - bankKpr.InitRestrictedDenoms(req.RestrictedDenoms...) // Parse and set genesis state balances for _, bal := range genState.Balances { @@ -231,6 +229,9 @@ func InitChainer( resHandler(ctx, tx, res) } + + // Intialize the bank's restricted denominations AFTER executing any genesis transactions. + bankKpr.InitRestrictedDenoms(req.RestrictedDenoms...) } // Done! From 844dc2dd12da7466e77f897c95a7157ebd8c2120 Mon Sep 17 00:00:00 2001 From: deelawn Date: Fri, 9 Aug 2024 10:14:35 -0700 Subject: [PATCH 07/15] moved restricted denoms to consensus params and persisted it to the database --- gno.land/cmd/gnoland/genesis_generate.go | 2 +- gno.land/cmd/gnoland/start.go | 3 +++ gno.land/pkg/gnoland/app.go | 21 ++++++++++++++++++--- tm2/pkg/bft/abci/types/abci.proto | 5 +++++ tm2/pkg/bft/abci/types/params.go | 5 +++++ tm2/pkg/bft/abci/types/types.go | 16 ++++++++++------ tm2/pkg/bft/consensus/replay.go | 11 +++++------ tm2/pkg/bft/types/genesis.go | 13 ++++++------- tm2/pkg/bft/types/params.go | 11 ++++++----- tm2/pkg/sdk/bank/keeper.go | 4 ++-- 10 files changed, 61 insertions(+), 30 deletions(-) diff --git a/gno.land/cmd/gnoland/genesis_generate.go b/gno.land/cmd/gnoland/genesis_generate.go index 43f4ed833c6..d4471961f0a 100644 --- a/gno.land/cmd/gnoland/genesis_generate.go +++ b/gno.land/cmd/gnoland/genesis_generate.go @@ -134,7 +134,7 @@ func execGenerate(cfg *generateCfg, io commands.IO) error { } if !cfg.ugnotUnrestricted { - genesis.RestrictedDenoms = []string{"ugnot"} + genesis.ConsensusParams.Account.RestrictedDenoms = []string{"ugnot"} } // Validate the genesis diff --git a/gno.land/cmd/gnoland/start.go b/gno.land/cmd/gnoland/start.go index 21f0cb4b1a6..e1edc9be7f9 100644 --- a/gno.land/cmd/gnoland/start.go +++ b/gno.land/cmd/gnoland/start.go @@ -385,6 +385,9 @@ func generateGenesisFile(genesisFile string, pk crypto.PubKey, c *startCfg) erro MaxGas: 100_000_000, // 100M gas TimeIotaMS: 100, // 100ms }, + Account: &abci.AccountParams{ + RestrictedDenoms: []string{"ugnot"}, + }, } gen.Validators = []bft.GenesisValidator{ diff --git a/gno.land/pkg/gnoland/app.go b/gno.land/pkg/gnoland/app.go index 98128357bae..72d2f881813 100644 --- a/gno.land/pkg/gnoland/app.go +++ b/gno.land/pkg/gnoland/app.go @@ -5,6 +5,7 @@ import ( "log/slog" "path/filepath" "strconv" + "strings" "github.com/gnolang/gno/gno.land/pkg/sdk/vm" "github.com/gnolang/gno/gnovm/pkg/gnoenv" @@ -28,6 +29,8 @@ import ( "github.com/gnolang/gno/tm2/pkg/db/memdb" ) +var restrictedDenomsKey = []byte("restricted_denoms") + type AppOptions struct { DB dbm.DB // `gnoRootDir` should point to the local location of the gno repository. @@ -89,12 +92,18 @@ func NewAppWithOptions(cfg *AppOptions) (abci.Application, error) { acctKpr := auth.NewAccountKeeper(mainKey, ProtoGnoAccount) bankKpr := bank.NewBankKeeper(acctKpr) + // If this isn't genesis, then there should be restricted denominations in the database. + if rawDenoms := cfg.DB.Get(restrictedDenomsKey); len(rawDenoms) != 0 { + denomList := strings.Split(string(rawDenoms), ",") + bankKpr.SetRestrictedDenoms(denomList...) + } + // XXX: Embed this ? stdlibsDir := filepath.Join(cfg.GnoRootDir, "gnovm", "stdlibs") vmk := vm.NewVMKeeper(baseKey, mainKey, acctKpr, bankKpr, stdlibsDir, cfg.MaxCycles) // Set InitChainer - app.SetInitChainer(InitChainer(app, acctKpr, bankKpr, cfg.GenesisTxHandler)) + app.SetInitChainer(InitChainer(app, acctKpr, bankKpr, cfg.GenesisTxHandler, cfg.DB)) // Set AnteHandler authOptions := auth.AnteOptions{ @@ -191,6 +200,7 @@ func InitChainer( acctKpr auth.AccountKeeperI, bankKpr bank.BankKeeperI, resHandler GenesisTxHandler, + db dbm.DB, ) func(sdk.Context, abci.RequestInitChain) abci.ResponseInitChain { return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { txResponses := []abci.ResponseDeliverTx{} @@ -230,8 +240,13 @@ func InitChainer( resHandler(ctx, tx, res) } - // Intialize the bank's restricted denominations AFTER executing any genesis transactions. - bankKpr.InitRestrictedDenoms(req.RestrictedDenoms...) + if denoms := req.ConsensusParams.Account.RestrictedDenoms; len(denoms) > 0 { + // Intialize the bank's restricted denominations AFTER executing any genesis transactions. + bankKpr.SetRestrictedDenoms(denoms...) + + // Put the restricted denominations in the database so they are available on restart. + db.SetSync(restrictedDenomsKey, []byte(strings.Join(denoms, ","))) + } } // Done! diff --git a/tm2/pkg/bft/abci/types/abci.proto b/tm2/pkg/bft/abci/types/abci.proto index 15b8ffa219e..5c37b46e3f7 100644 --- a/tm2/pkg/bft/abci/types/abci.proto +++ b/tm2/pkg/bft/abci/types/abci.proto @@ -157,6 +157,7 @@ message StringError { message ConsensusParams { BlockParams block = 1 [json_name = "Block"]; ValidatorParams validator = 2 [json_name = "Validator"]; + AccountParams account = 3 [json_name = "Account"]; } message BlockParams { @@ -177,6 +178,10 @@ message ValidatorUpdate { sint64 power = 3 [json_name = "Power"]; } +message AccountParams { + repeated string restricted_denoms = 1 [json_name = "RestrictedDenoms"]; +} + message LastCommitInfo { sint32 round = 1 [json_name = "Round"]; repeated VoteInfo votes = 2 [json_name = "Votes"]; diff --git a/tm2/pkg/bft/abci/types/params.go b/tm2/pkg/bft/abci/types/params.go index ac714d92dcd..24c70d25b20 100644 --- a/tm2/pkg/bft/abci/types/params.go +++ b/tm2/pkg/bft/abci/types/params.go @@ -29,9 +29,14 @@ func (params ConsensusParams) Update(params2 ConsensusParams) ConsensusParams { if params2.Block != nil { res.Block = amino.DeepCopy(params2.Block).(*BlockParams) } + if params2.Validator != nil { res.Validator = amino.DeepCopy(params2.Validator).(*ValidatorParams) } + if params2.Account != nil { + res.Account = amino.DeepCopy(params2.Account).(*AccountParams) + } + return res } diff --git a/tm2/pkg/bft/abci/types/types.go b/tm2/pkg/bft/abci/types/types.go index 19a4f1e3bc4..7c7d9466050 100644 --- a/tm2/pkg/bft/abci/types/types.go +++ b/tm2/pkg/bft/abci/types/types.go @@ -41,12 +41,11 @@ type RequestSetOption struct { type RequestInitChain struct { RequestBase - Time time.Time - ChainID string - ConsensusParams *ConsensusParams - Validators []ValidatorUpdate - RestrictedDenoms []string - AppState interface{} + Time time.Time + ChainID string + ConsensusParams *ConsensusParams + Validators []ValidatorUpdate + AppState interface{} } type RequestQuery struct { @@ -246,6 +245,7 @@ func (err EventString) Event() string { type ConsensusParams struct { Block *BlockParams Validator *ValidatorParams + Account *AccountParams } type BlockParams struct { @@ -266,6 +266,10 @@ type ValidatorUpdate struct { Power int64 } +type AccountParams struct { + RestrictedDenoms []string +} + type LastCommitInfo struct { Round int32 Votes []VoteInfo diff --git a/tm2/pkg/bft/consensus/replay.go b/tm2/pkg/bft/consensus/replay.go index f9607cc2474..02e6dade72c 100644 --- a/tm2/pkg/bft/consensus/replay.go +++ b/tm2/pkg/bft/consensus/replay.go @@ -296,12 +296,11 @@ func (h *Handshaker) ReplayBlocks( nextVals := validatorSet.ABCIValidatorUpdates() csParams := h.genDoc.ConsensusParams req := abci.RequestInitChain{ - Time: h.genDoc.GenesisTime, - ChainID: h.genDoc.ChainID, - ConsensusParams: &csParams, - Validators: nextVals, - AppState: h.genDoc.AppState, - RestrictedDenoms: h.genDoc.RestrictedDenoms, + Time: h.genDoc.GenesisTime, + ChainID: h.genDoc.ChainID, + ConsensusParams: &csParams, + Validators: nextVals, + AppState: h.genDoc.AppState, } res, err := proxyApp.Consensus().InitChainSync(req) if err != nil { diff --git a/tm2/pkg/bft/types/genesis.go b/tm2/pkg/bft/types/genesis.go index 811830fb43e..c03f7acc09e 100644 --- a/tm2/pkg/bft/types/genesis.go +++ b/tm2/pkg/bft/types/genesis.go @@ -44,13 +44,12 @@ type GenesisValidator struct { // GenesisDoc defines the initial conditions for a tendermint blockchain, in particular its validator set. type GenesisDoc struct { - GenesisTime time.Time `json:"genesis_time"` - ChainID string `json:"chain_id"` - ConsensusParams abci.ConsensusParams `json:"consensus_params,omitempty"` - Validators []GenesisValidator `json:"validators,omitempty"` - RestrictedDenoms []string `json:"restricted_denoms,omitempty"` - AppHash []byte `json:"app_hash"` - AppState interface{} `json:"app_state,omitempty"` + GenesisTime time.Time `json:"genesis_time"` + ChainID string `json:"chain_id"` + ConsensusParams abci.ConsensusParams `json:"consensus_params,omitempty"` + Validators []GenesisValidator `json:"validators,omitempty"` + AppHash []byte `json:"app_hash"` + AppState interface{} `json:"app_state,omitempty"` } // SaveAs is a utility method for saving GenensisDoc as a JSON file. diff --git a/tm2/pkg/bft/types/params.go b/tm2/pkg/bft/types/params.go index 0b48da9329e..dc72bc9411e 100644 --- a/tm2/pkg/bft/types/params.go +++ b/tm2/pkg/bft/types/params.go @@ -36,8 +36,9 @@ var validatorPubKeyTypeURLs = map[string]struct{}{ func DefaultConsensusParams() abci.ConsensusParams { return abci.ConsensusParams{ - DefaultBlockParams(), - DefaultValidatorParams(), + Block: DefaultBlockParams(), + Validator: DefaultValidatorParams(), + Account: &abci.AccountParams{}, } } @@ -51,9 +52,9 @@ func DefaultBlockParams() *abci.BlockParams { } func DefaultValidatorParams() *abci.ValidatorParams { - return &abci.ValidatorParams{[]string{ - amino.GetTypeURL(ed25519.PubKeyEd25519{}), - }} + return &abci.ValidatorParams{ + PubKeyTypeURLs: []string{amino.GetTypeURL(ed25519.PubKeyEd25519{})}, + } } func ValidateConsensusParams(params abci.ConsensusParams) error { diff --git a/tm2/pkg/sdk/bank/keeper.go b/tm2/pkg/sdk/bank/keeper.go index c28cd060660..4e57439d25a 100644 --- a/tm2/pkg/sdk/bank/keeper.go +++ b/tm2/pkg/sdk/bank/keeper.go @@ -22,7 +22,7 @@ type BankKeeperI interface { AddCoins(ctx sdk.Context, addr crypto.Address, amt std.Coins) (std.Coins, error) SetCoins(ctx sdk.Context, addr crypto.Address, amt std.Coins) error - InitRestrictedDenoms(restrictedDenoms ...string) + SetRestrictedDenoms(restrictedDenoms ...string) } var _ BankKeeperI = BankKeeper{} @@ -56,7 +56,7 @@ func NewBankKeeperWithRestrictedDenoms(acck auth.AccountKeeper, restrictedDenoms return bankKeeper } -func (bank BankKeeper) InitRestrictedDenoms(restrictedDenoms ...string) { +func (bank BankKeeper) SetRestrictedDenoms(restrictedDenoms ...string) { for _, denom := range restrictedDenoms { bank.restrictedDenoms[denom] = struct{}{} } From 0553a8eb473de2ca9679879ba5ccf5eecec675cb Mon Sep 17 00:00:00 2001 From: deelawn Date: Fri, 9 Aug 2024 10:18:17 -0700 Subject: [PATCH 08/15] revert app name to original --- gno.land/pkg/gnoland/app.go | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/gno.land/pkg/gnoland/app.go b/gno.land/pkg/gnoland/app.go index 72d2f881813..173cb743adc 100644 --- a/gno.land/pkg/gnoland/app.go +++ b/gno.land/pkg/gnoland/app.go @@ -81,12 +81,12 @@ func NewAppWithOptions(cfg *AppOptions) (abci.Application, error) { // Create BaseApp. // TODO: Add a consensus based min gas prices for the node, by default it does not check - app := sdk.NewBaseApp("gnoland", cfg.Logger, cfg.DB, baseKey, mainKey) - app.SetAppVersion("dev") + baseApp := sdk.NewBaseApp("gnoland", cfg.Logger, cfg.DB, baseKey, mainKey) + baseApp.SetAppVersion("dev") // Set mounts for BaseApp's MultiStore. - app.MountStoreWithDB(mainKey, iavl.StoreConstructor, cfg.DB) - app.MountStoreWithDB(baseKey, dbadapter.StoreConstructor, cfg.DB) + baseApp.MountStoreWithDB(mainKey, iavl.StoreConstructor, cfg.DB) + baseApp.MountStoreWithDB(baseKey, dbadapter.StoreConstructor, cfg.DB) // Construct keepers. acctKpr := auth.NewAccountKeeper(mainKey, ProtoGnoAccount) @@ -103,7 +103,7 @@ func NewAppWithOptions(cfg *AppOptions) (abci.Application, error) { vmk := vm.NewVMKeeper(baseKey, mainKey, acctKpr, bankKpr, stdlibsDir, cfg.MaxCycles) // Set InitChainer - app.SetInitChainer(InitChainer(app, acctKpr, bankKpr, cfg.GenesisTxHandler, cfg.DB)) + baseApp.SetInitChainer(InitChainer(baseApp, acctKpr, bankKpr, cfg.GenesisTxHandler, cfg.DB)) // Set AnteHandler authOptions := auth.AnteOptions{ @@ -111,7 +111,7 @@ func NewAppWithOptions(cfg *AppOptions) (abci.Application, error) { } authAnteHandler := auth.NewAnteHandler( acctKpr, bankKpr, auth.DefaultSigVerificationGasConsumer, authOptions) - app.SetAnteHandler( + baseApp.SetAnteHandler( // Override default AnteHandler with custom logic. func(ctx sdk.Context, tx std.Tx, simulate bool) ( newCtx sdk.Context, res sdk.Result, abort bool, @@ -132,30 +132,30 @@ func NewAppWithOptions(cfg *AppOptions) (abci.Application, error) { ) // Set EndBlocker - app.SetEndBlocker( + baseApp.SetEndBlocker( EndBlocker( c, vmk, - app, + baseApp, ), ) // Set a handler Route. - app.Router().AddRoute("auth", auth.NewHandler(acctKpr)) - app.Router().AddRoute("bank", bank.NewHandler(bankKpr)) - app.Router().AddRoute("vm", vm.NewHandler(vmk)) + baseApp.Router().AddRoute("auth", auth.NewHandler(acctKpr)) + baseApp.Router().AddRoute("bank", bank.NewHandler(bankKpr)) + baseApp.Router().AddRoute("vm", vm.NewHandler(vmk)) // Load latest version. - if err := app.LoadLatestVersion(); err != nil { + if err := baseApp.LoadLatestVersion(); err != nil { return nil, err } // Initialize the VMKeeper. - ms := app.GetCacheMultiStore() + ms := baseApp.GetCacheMultiStore() vmk.Initialize(cfg.Logger, ms, cfg.CacheStdlibLoad) ms.MultiWrite() // XXX why was't this needed? - return app, nil + return baseApp, nil } // NewApp creates the GnoLand application. From dd61315bf397531a2f88ab46d9226a5916998ddd Mon Sep 17 00:00:00 2001 From: deelawn Date: Fri, 9 Aug 2024 10:25:37 -0700 Subject: [PATCH 09/15] cleanup --- gno.land/pkg/gnoland/app.go | 2 +- tm2/pkg/bft/node/node.go | 4 ---- tm2/pkg/sdk/bank/keeper.go | 11 ----------- tm2/pkg/sdk/bank/keeper_test.go | 3 ++- 4 files changed, 3 insertions(+), 17 deletions(-) diff --git a/gno.land/pkg/gnoland/app.go b/gno.land/pkg/gnoland/app.go index 173cb743adc..3b4b086d066 100644 --- a/gno.land/pkg/gnoland/app.go +++ b/gno.land/pkg/gnoland/app.go @@ -92,7 +92,7 @@ func NewAppWithOptions(cfg *AppOptions) (abci.Application, error) { acctKpr := auth.NewAccountKeeper(mainKey, ProtoGnoAccount) bankKpr := bank.NewBankKeeper(acctKpr) - // If this isn't genesis, then there should be restricted denominations in the database. + // If this isn't genesis, then there might be restricted denominations in the database. if rawDenoms := cfg.DB.Get(restrictedDenomsKey); len(rawDenoms) != 0 { denomList := strings.Split(string(rawDenoms), ",") bankKpr.SetRestrictedDenoms(denomList...) diff --git a/tm2/pkg/bft/node/node.go b/tm2/pkg/bft/node/node.go index 229f546beec..e29de3dd1ae 100644 --- a/tm2/pkg/bft/node/node.go +++ b/tm2/pkg/bft/node/node.go @@ -177,10 +177,6 @@ type Node struct { firstBlockSignal <-chan struct{} } -func (n *Node) GenensisDoc() types.GenesisDoc { - return *n.genesisDoc -} - func initDBs(config *cfg.Config, dbProvider DBProvider) (blockStore *store.BlockStore, stateDB dbm.DB, err error) { var blockStoreDB dbm.DB blockStoreDB, err = dbProvider(&DBContext{"blockstore", config}) diff --git a/tm2/pkg/sdk/bank/keeper.go b/tm2/pkg/sdk/bank/keeper.go index 4e57439d25a..9e771d9a724 100644 --- a/tm2/pkg/sdk/bank/keeper.go +++ b/tm2/pkg/sdk/bank/keeper.go @@ -45,17 +45,6 @@ func NewBankKeeper(acck auth.AccountKeeper) BankKeeper { } } -func NewBankKeeperWithRestrictedDenoms(acck auth.AccountKeeper, restrictedDenoms ...string) BankKeeper { - restrictedDenomsStore := make(map[string]struct{}, len(restrictedDenoms)) - for _, denom := range restrictedDenoms { - restrictedDenomsStore[denom] = struct{}{} - } - - bankKeeper := NewBankKeeper(acck) - bankKeeper.restrictedDenoms = restrictedDenomsStore - return bankKeeper -} - func (bank BankKeeper) SetRestrictedDenoms(restrictedDenoms ...string) { for _, denom := range restrictedDenoms { bank.restrictedDenoms[denom] = struct{}{} diff --git a/tm2/pkg/sdk/bank/keeper_test.go b/tm2/pkg/sdk/bank/keeper_test.go index fdf77fab8eb..c6e389b40dc 100644 --- a/tm2/pkg/sdk/bank/keeper_test.go +++ b/tm2/pkg/sdk/bank/keeper_test.go @@ -141,7 +141,8 @@ func TestBankKeeperWithRestrictions(t *testing.T) { env := setupTestEnv() ctx := env.ctx - bankKeeper := NewBankKeeperWithRestrictedDenoms(env.acck, "foocoin") + bankKeeper := NewBankKeeper(env.acck) + bankKeeper.SetRestrictedDenoms("foocoin") addr := crypto.AddressFromPreimage([]byte("addr1")) addr2 := crypto.AddressFromPreimage([]byte("addr2")) acc := env.acck.NewAccountWithAddress(ctx, addr) From f21a9e27c838b24a1f37b70704c464fda3fad645 Mon Sep 17 00:00:00 2001 From: deelawn Date: Fri, 9 Aug 2024 15:18:14 -0700 Subject: [PATCH 10/15] lint --- gno.land/pkg/gnoland/app.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gno.land/pkg/gnoland/app.go b/gno.land/pkg/gnoland/app.go index 3b4b086d066..015c7aa27d7 100644 --- a/gno.land/pkg/gnoland/app.go +++ b/gno.land/pkg/gnoland/app.go @@ -241,7 +241,7 @@ func InitChainer( } if denoms := req.ConsensusParams.Account.RestrictedDenoms; len(denoms) > 0 { - // Intialize the bank's restricted denominations AFTER executing any genesis transactions. + // Set the bank's restricted denominations AFTER executing any genesis transactions. bankKpr.SetRestrictedDenoms(denoms...) // Put the restricted denominations in the database so they are available on restart. From 4f21ff1a890ea024cf6a67553cc2639661b9e0a1 Mon Sep 17 00:00:00 2001 From: deelawn Date: Fri, 9 Aug 2024 15:28:57 -0700 Subject: [PATCH 11/15] small improvements --- gno.land/pkg/gnoland/app.go | 2 +- tm2/pkg/sdk/bank/keeper.go | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/gno.land/pkg/gnoland/app.go b/gno.land/pkg/gnoland/app.go index 015c7aa27d7..2bbaa415bf0 100644 --- a/gno.land/pkg/gnoland/app.go +++ b/gno.land/pkg/gnoland/app.go @@ -240,7 +240,7 @@ func InitChainer( resHandler(ctx, tx, res) } - if denoms := req.ConsensusParams.Account.RestrictedDenoms; len(denoms) > 0 { + if denoms := req.ConsensusParams.Account.RestrictedDenoms; len(denoms) != 0 { // Set the bank's restricted denominations AFTER executing any genesis transactions. bankKpr.SetRestrictedDenoms(denoms...) diff --git a/tm2/pkg/sdk/bank/keeper.go b/tm2/pkg/sdk/bank/keeper.go index 9e771d9a724..d50b68334b5 100644 --- a/tm2/pkg/sdk/bank/keeper.go +++ b/tm2/pkg/sdk/bank/keeper.go @@ -100,6 +100,11 @@ func (bank BankKeeper) InputOutputCoins(ctx sdk.Context, inputs []Input, outputs // 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 + } + // Coins of a restricted denomination cannot be transferred unless the account is unlocked. if amt.ContainOneOfDenom(bank.restrictedDenoms) { if acc := bank.acck.GetAccount(ctx, addr); acc != nil && acc.IsLocked() { From d670a3721553c996de808b3313b882c45e806b34 Mon Sep 17 00:00:00 2001 From: deelawn Date: Fri, 9 Aug 2024 16:17:55 -0700 Subject: [PATCH 12/15] define denom in default genesis --- gno.land/cmd/gnoland/genesis_generate.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/gno.land/cmd/gnoland/genesis_generate.go b/gno.land/cmd/gnoland/genesis_generate.go index d4471961f0a..9333d3b3bc9 100644 --- a/gno.land/cmd/gnoland/genesis_generate.go +++ b/gno.land/cmd/gnoland/genesis_generate.go @@ -133,8 +133,8 @@ func execGenerate(cfg *generateCfg, io commands.IO) error { genesis.ConsensusParams.Block.TimeIotaMS = cfg.blockTimeIota } - if !cfg.ugnotUnrestricted { - genesis.ConsensusParams.Account.RestrictedDenoms = []string{"ugnot"} + if cfg.ugnotUnrestricted { + genesis.ConsensusParams.Account.RestrictedDenoms = []string{} } // Validate the genesis @@ -157,9 +157,12 @@ func execGenerate(cfg *generateCfg, io commands.IO) error { // getDefaultGenesis returns the default genesis config func getDefaultGenesis() *types.GenesisDoc { - return &types.GenesisDoc{ + genDoc := &types.GenesisDoc{ GenesisTime: time.Now(), ChainID: defaultChainID, ConsensusParams: types.DefaultConsensusParams(), } + + genDoc.ConsensusParams.Account.RestrictedDenoms = []string{"ugnot"} + return genDoc } From 9b1058784ed8af6fe9523d40f4bc491e623cdfec Mon Sep 17 00:00:00 2001 From: deelawn Date: Tue, 13 Aug 2024 16:49:33 -0700 Subject: [PATCH 13/15] add ability to disable ugnot restrictions for lazy init --- gno.land/cmd/gnoland/start.go | 38 +++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/gno.land/cmd/gnoland/start.go b/gno.land/cmd/gnoland/start.go index e1edc9be7f9..960ba3df296 100644 --- a/gno.land/cmd/gnoland/start.go +++ b/gno.land/cmd/gnoland/start.go @@ -43,17 +43,18 @@ var startGraphic = strings.ReplaceAll(` `, "'", "`") type startCfg struct { - gnoRootDir string // TODO: remove as part of https://github.com/gnolang/gno/issues/1952 - skipFailingGenesisTxs bool // TODO: remove as part of https://github.com/gnolang/gno/issues/1952 - genesisBalancesFile string // TODO: remove as part of https://github.com/gnolang/gno/issues/1952 - genesisTxsFile string // TODO: remove as part of https://github.com/gnolang/gno/issues/1952 - genesisRemote string // TODO: remove as part of https://github.com/gnolang/gno/issues/1952 - genesisFile string - chainID string - dataDir string - genesisMaxVMCycles int64 - config string - lazyInit bool + gnoRootDir string // TODO: remove as part of https://github.com/gnolang/gno/issues/1952 + skipFailingGenesisTxs bool // TODO: remove as part of https://github.com/gnolang/gno/issues/1952 + genesisBalancesFile string // TODO: remove as part of https://github.com/gnolang/gno/issues/1952 + genesisTxsFile string // TODO: remove as part of https://github.com/gnolang/gno/issues/1952 + genesisRemote string // TODO: remove as part of https://github.com/gnolang/gno/issues/1952 + genesisFile string + chainID string + dataDir string + genesisMaxVMCycles int64 + config string + lazyInit bool + disableUGNOTRestrictions bool logLevel string logFormat string @@ -171,6 +172,13 @@ func (c *startCfg) RegisterFlags(fs *flag.FlagSet) { false, "flag indicating if lazy init is enabled. Generates the node secrets, configuration, and genesis.json", ) + + fs.BoolVar( + &c.disableUGNOTRestrictions, + "disable-ugnot-restrictions", + false, + "if true and genesis file is not provided, disables ugnot sending restrictions", + ) } func execStart(ctx context.Context, c *startCfg, io commands.IO) error { @@ -228,6 +236,10 @@ func execStart(ctx context.Context, c *startCfg, io commands.IO) error { if err := lazyInitGenesis(io, c, genesisPath, privateKey.GetPubKey()); err != nil { return fmt.Errorf("unable to initialize genesis.json, %w", err) } + } else if c.disableUGNOTRestrictions { + // Clearly return an error in this case so the user isn't left wondering why + // the restrictions weren't disabled. + return errors.New("cannot disable ugnot restrictions when genesis file is provided") } // Initialize telemetry @@ -390,6 +402,10 @@ func generateGenesisFile(genesisFile string, pk crypto.PubKey, c *startCfg) erro }, } + if c.disableUGNOTRestrictions { + gen.ConsensusParams.Account.RestrictedDenoms = nil + } + gen.Validators = []bft.GenesisValidator{ { Address: pk.Address(), From 5db4399acb8a226fd4cfa56964c6de89a6bab835 Mon Sep 17 00:00:00 2001 From: deelawn Date: Tue, 13 Aug 2024 16:49:57 -0700 Subject: [PATCH 14/15] allow ugnot restrictions to be disabled for integration tests --- gno.land/pkg/integration/testing_integration.go | 5 +++++ gno.land/pkg/integration/testing_node.go | 1 + 2 files changed, 6 insertions(+) diff --git a/gno.land/pkg/integration/testing_integration.go b/gno.land/pkg/integration/testing_integration.go index 0462b0c7639..b86181cfb21 100644 --- a/gno.land/pkg/integration/testing_integration.go +++ b/gno.land/pkg/integration/testing_integration.go @@ -8,6 +8,7 @@ import ( "log/slog" "os" "path/filepath" + "slices" "strconv" "strings" "testing" @@ -183,6 +184,10 @@ func setupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params { // Generate config and node cfg := TestingMinimalNodeConfig(t, gnoRootDir) + if slices.Contains[[]string, string](args, "--ugnot-locked") { + cfg.Genesis.ConsensusParams.Account.RestrictedDenoms = []string{"ugnot"} + } + genesis := ts.Value(envKeyGenesis).(*gnoland.GnoGenesisState) genesis.Txs = append(pkgsTxs, genesis.Txs...) diff --git a/gno.land/pkg/integration/testing_node.go b/gno.land/pkg/integration/testing_node.go index 993386f6b04..1a6b627d913 100644 --- a/gno.land/pkg/integration/testing_node.go +++ b/gno.land/pkg/integration/testing_node.go @@ -89,6 +89,7 @@ func DefaultTestingGenesisConfig(t TestingTS, gnoroot string, self crypto.PubKey MaxGas: 100_000_000, // 100M gas TimeIotaMS: 100, // 100ms }, + Account: new(abci.AccountParams), }, Validators: []bft.GenesisValidator{ { From e364c6d8af42ce31cfc5a546c70fd7e334de0da9 Mon Sep 17 00:00:00 2001 From: deelawn Date: Tue, 13 Aug 2024 16:50:23 -0700 Subject: [PATCH 15/15] txtar for ugnot send restriction --- gno.land/cmd/gnoland/dylan/send.txtar | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 gno.land/cmd/gnoland/dylan/send.txtar diff --git a/gno.land/cmd/gnoland/dylan/send.txtar b/gno.land/cmd/gnoland/dylan/send.txtar new file mode 100644 index 00000000000..c11240aa06f --- /dev/null +++ b/gno.land/cmd/gnoland/dylan/send.txtar @@ -0,0 +1,4 @@ +gnoland start --ugnot-locked + +! gnokey maketx send -send "9999999ugnot" -to g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5 -gas-fee 1ugnot -gas-wanted 10000000 -broadcast -chainid=tendermint_test test1 +stderr 'account is locked' \ No newline at end of file