diff --git a/examples/gno.land/r/sys/params/params.gno b/examples/gno.land/r/sys/params/params.gno index 2a60b081dab..dc175fc0e3f 100644 --- a/examples/gno.land/r/sys/params/params.gno +++ b/examples/gno.land/r/sys/params/params.gno @@ -87,14 +87,14 @@ func NewSysParamStringsPropRequestWithTitle(module, submodule, name, title strin func NewSysParamStringsPropRequestAddWithTitle(module, submodule, name, title string, value []string) dao.ProposalRequest { return newPropRequest( syskey(module, submodule, name), - func() { prms.UpdateSysParamStrings(module, submodule, name, value, true) }, + func() { prms.AddUniqueSysParamStrings(module, submodule, name, value) }, title, ) } func NewSysParamStringsPropRequestRemoveWithTitle(module, submodule, name, title string, value []string) dao.ProposalRequest { return newPropRequest( syskey(module, submodule, name), - func() { prms.UpdateSysParamStrings(module, submodule, name, value, false) }, + func() { prms.RemoveSysParamStrings(module, submodule, name, value) }, title, ) } diff --git a/gno.land/pkg/sdk/vm/builtins.go b/gno.land/pkg/sdk/vm/builtins.go index 1545f3b0cdb..7bb9935b992 100644 --- a/gno.land/pkg/sdk/vm/builtins.go +++ b/gno.land/pkg/sdk/vm/builtins.go @@ -111,40 +111,15 @@ func (prm *SDKParams) SetStrings(key string, value []string) { } func (prm *SDKParams) UpdateStrings(key string, vals []string, add bool) { - ss := &[]string{} - prm.pmk.GetStrings(prm.ctx, key, ss) - - oldList := *ss - existing := make(map[string]struct{}, len(oldList)) - // Temporary map for duplicate detection - for _, s := range oldList { - existing[s] = struct{}{} - } + prm.pmk.AddUniqueStrings(prm.ctx, key, vals) +} - if add { - // Append only non-duplicate values - for _, v := range vals { - if _, found := existing[v]; !found { - oldList = append(oldList, v) - existing[v] = struct{}{} - } - } - prm.SetStrings(key, oldList) - return - } - // Remove case - updatedList := oldList[:0] // reuse original memory - removeSet := make(map[string]struct{}, len(vals)) - for _, v := range vals { - removeSet[v] = struct{}{} - } +func (prm *SDKParams) AddUniqueStrings(key string, vals []string) { + prm.pmk.AddUniqueStrings(prm.ctx, key, vals) +} - for _, s := range oldList { - if _, found := removeSet[s]; !found { - updatedList = append(updatedList, s) - } - } - prm.SetStrings(key, updatedList) +func (prm *SDKParams) RemoveStrings(key string, vals []string) { + prm.pmk.RemoveStrings(prm.ctx, key, vals) } func (prm *SDKParams) willSetKeeperParams(ctx sdk.Context, key string, value any) { diff --git a/gno.land/pkg/sdk/vm/params.go b/gno.land/pkg/sdk/vm/params.go index 22636904280..b92199ed10c 100644 --- a/gno.land/pkg/sdk/vm/params.go +++ b/gno.land/pkg/sdk/vm/params.go @@ -120,3 +120,7 @@ func (vm *VMKeeper) getSysNamesPkgParam(ctx sdk.Context) string { func (vm *VMKeeper) WillSetParam(ctx sdk.Context, key string, value any) { // XXX validate input? } + +func (vm *VMKeeper) WillUpdateParam(ctx sdk.Context, key string, value any, add bool) { + // XXX +} diff --git a/gnovm/cmd/gno/internal/fix/stdsplit.go b/gnovm/cmd/gno/internal/fix/stdsplit.go index 890400098cd..6cfacb0faa8 100644 --- a/gnovm/cmd/gno/internal/fix/stdsplit.go +++ b/gnovm/cmd/gno/internal/fix/stdsplit.go @@ -65,11 +65,12 @@ func makeSplitFuncs() { "std.BankerTypeRealmSend": newSplitFunc("chain/banker.BankerTypeRealmSend"), "std.BankerTypeRealmIssue": newSplitFunc("chain/banker.BankerTypeRealmIssue"), - "std.SetParamBool": newSplitFunc("chain/params.SetBool"), - "std.SetParamBytes": newSplitFunc("chain/params.SetBytes"), - "std.SetParamInt64": newSplitFunc("chain/params.SetInt64"), - "std.SetParamString": newSplitFunc("chain/params.SetString"), - "std.SetParamStrings": newSplitFunc("chain/params.SetStrings"), + "std.SetParamBool": newSplitFunc("chain/params.SetBool"), + "std.SetParamBytes": newSplitFunc("chain/params.SetBytes"), + "std.SetParamInt64": newSplitFunc("chain/params.SetInt64"), + "std.SetParamString": newSplitFunc("chain/params.SetString"), + "std.SetParamStrings": newSplitFunc("chain/params.SetStrings"), + // XXX "std.UpdateParamStrings": newSplitFunc("chain/params.UpdateStrings"), "std.SetParamUint64": newSplitFunc("chain/params.SetUint64"), diff --git a/gnovm/pkg/test/test.go b/gnovm/pkg/test/test.go index d90d6a17710..3b58977c764 100644 --- a/gnovm/pkg/test/test.go +++ b/gnovm/pkg/test/test.go @@ -105,13 +105,14 @@ func newTestParams() *testParams { return &testParams{} } -func (tp *testParams) SetBool(key string, val bool) { /* noop */ } -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) SetStrings(key string, val []string) { /* noop */ } -func (tp *testParams) UpdateStrings(key string, val []string, add bool) { /* noop */ } +func (tp *testParams) SetBool(key string, val bool) { /* noop */ } +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) SetStrings(key string, val []string) { /* noop */ } +func (tp *testParams) AddUniqueStrings(key string, val []string) { /* noop */ } +func (tp *testParams) RemoveStrings(key string, val []string) { /* noop */ } // ---------------------------------------- // main test function diff --git a/gnovm/stdlibs/chain/params/params.gno b/gnovm/stdlibs/chain/params/params.gno index 800e262fa92..e44b9beba94 100644 --- a/gnovm/stdlibs/chain/params/params.gno +++ b/gnovm/stdlibs/chain/params/params.gno @@ -9,4 +9,5 @@ func SetInt64(key string, val int64) func SetUint64(key string, val uint64) func SetBytes(key string, val []byte) func SetStrings(key string, val []string) -func UpdateParamStrings(key string, val []string, add bool) +func AddUniqueParamStrings(key string, val []string) +func RemoveParamStrings(key string, val []string) diff --git a/gnovm/stdlibs/chain/params/params.go b/gnovm/stdlibs/chain/params/params.go index bcaa320c38d..074fc0a8eea 100644 --- a/gnovm/stdlibs/chain/params/params.go +++ b/gnovm/stdlibs/chain/params/params.go @@ -42,9 +42,14 @@ func SetStrings(m *gno.Machine, key string, val []string) { execctx.GetContext(m).Params.SetStrings(pk, val) } -func UpdateParamStrings(m *gno.Machine, key string, val []string, add bool) { +func AddUniqueParamStrings(m *gno.Machine, key string, val []string) { pk := pkey(m, key) - execctx.GetContext(m).Params.UpdateStrings(pk, val, add) + execctx.GetContext(m).Params.AddUniqueStrings(pk, val) +} + +func RemoveParamStrings(m *gno.Machine, key string, val []string) { + pk := pkey(m, key) + execctx.GetContext(m).Params.RemoveStrings(pk, val) } // NOTE: further validation must happen by implementor of ParamsInterface. diff --git a/gnovm/stdlibs/generated.go b/gnovm/stdlibs/generated.go index 974f1dcabca..773ad5bcea7 100644 --- a/gnovm/stdlibs/generated.go +++ b/gnovm/stdlibs/generated.go @@ -562,11 +562,40 @@ var nativeFuncs = [...]NativeFunc{ }, { "chain/params", - "UpdateParamStrings", + "AddUniqueParamStrings", + []gno.FieldTypeExpr{ + {NameExpr: *gno.Nx("p0"), Type: gno.X("string")}, + {NameExpr: *gno.Nx("p1"), Type: gno.X("[]string")}, + }, + []gno.FieldTypeExpr{}, + true, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 string + rp0 = reflect.ValueOf(&p0).Elem() + p1 []string + rp1 = reflect.ValueOf(&p1).Elem() + ) + + tv0 := b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV + tv0.DeepFill(m.Store) + gno.Gno2GoValue(tv0, rp0) + tv1 := b.GetPointerTo(nil, gno.NewValuePathBlock(1, 1, "")).TV + tv1.DeepFill(m.Store) + gno.Gno2GoValue(tv1, rp1) + + libs_chain_params.AddUniqueParamStrings( + m, + p0, p1) + }, + }, + { + "chain/params", + "RemoveParamStrings", []gno.FieldTypeExpr{ {NameExpr: *gno.Nx("p0"), Type: gno.X("string")}, {NameExpr: *gno.Nx("p1"), Type: gno.X("[]string")}, - {NameExpr: *gno.Nx("p2"), Type: gno.X("bool")}, }, []gno.FieldTypeExpr{}, true, @@ -577,8 +606,6 @@ var nativeFuncs = [...]NativeFunc{ rp0 = reflect.ValueOf(&p0).Elem() p1 []string rp1 = reflect.ValueOf(&p1).Elem() - p2 bool - rp2 = reflect.ValueOf(&p2).Elem() ) tv0 := b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV @@ -587,13 +614,10 @@ var nativeFuncs = [...]NativeFunc{ tv1 := b.GetPointerTo(nil, gno.NewValuePathBlock(1, 1, "")).TV tv1.DeepFill(m.Store) gno.Gno2GoValue(tv1, rp1) - tv2 := b.GetPointerTo(nil, gno.NewValuePathBlock(1, 2, "")).TV - tv2.DeepFill(m.Store) - gno.Gno2GoValue(tv2, rp2) - libs_chain_params.UpdateParamStrings( + libs_chain_params.RemoveParamStrings( m, - p0, p1, p2) + p0, p1) }, }, { @@ -1204,13 +1228,12 @@ var nativeFuncs = [...]NativeFunc{ }, { "sys/params", - "updateSysParamStrings", + "addUniqueSysParamStrings", []gno.FieldTypeExpr{ {NameExpr: *gno.Nx("p0"), Type: gno.X("string")}, {NameExpr: *gno.Nx("p1"), Type: gno.X("string")}, {NameExpr: *gno.Nx("p2"), Type: gno.X("string")}, {NameExpr: *gno.Nx("p3"), Type: gno.X("[]string")}, - {NameExpr: *gno.Nx("p4"), Type: gno.X("bool")}, }, []gno.FieldTypeExpr{}, true, @@ -1225,8 +1248,6 @@ var nativeFuncs = [...]NativeFunc{ rp2 = reflect.ValueOf(&p2).Elem() p3 []string rp3 = reflect.ValueOf(&p3).Elem() - p4 bool - rp4 = reflect.ValueOf(&p4).Elem() ) tv0 := b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV @@ -1241,13 +1262,52 @@ var nativeFuncs = [...]NativeFunc{ tv3 := b.GetPointerTo(nil, gno.NewValuePathBlock(1, 3, "")).TV tv3.DeepFill(m.Store) gno.Gno2GoValue(tv3, rp3) - tv4 := b.GetPointerTo(nil, gno.NewValuePathBlock(1, 4, "")).TV - tv4.DeepFill(m.Store) - gno.Gno2GoValue(tv4, rp4) - libs_sys_params.X_updateSysParamStrings( + libs_sys_params.X_addUniqueSysParamStrings( m, - p0, p1, p2, p3, p4) + p0, p1, p2, p3) + }, + }, + { + "sys/params", + "removeSysParamStrings", + []gno.FieldTypeExpr{ + {NameExpr: *gno.Nx("p0"), Type: gno.X("string")}, + {NameExpr: *gno.Nx("p1"), Type: gno.X("string")}, + {NameExpr: *gno.Nx("p2"), Type: gno.X("string")}, + {NameExpr: *gno.Nx("p3"), Type: gno.X("[]string")}, + }, + []gno.FieldTypeExpr{}, + true, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 string + rp0 = reflect.ValueOf(&p0).Elem() + p1 string + rp1 = reflect.ValueOf(&p1).Elem() + p2 string + rp2 = reflect.ValueOf(&p2).Elem() + p3 []string + rp3 = reflect.ValueOf(&p3).Elem() + ) + + tv0 := b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV + tv0.DeepFill(m.Store) + gno.Gno2GoValue(tv0, rp0) + tv1 := b.GetPointerTo(nil, gno.NewValuePathBlock(1, 1, "")).TV + tv1.DeepFill(m.Store) + gno.Gno2GoValue(tv1, rp1) + tv2 := b.GetPointerTo(nil, gno.NewValuePathBlock(1, 2, "")).TV + tv2.DeepFill(m.Store) + gno.Gno2GoValue(tv2, rp2) + tv3 := b.GetPointerTo(nil, gno.NewValuePathBlock(1, 3, "")).TV + tv3.DeepFill(m.Store) + gno.Gno2GoValue(tv3, rp3) + + libs_sys_params.X_removeSysParamStrings( + m, + p0, p1, p2, p3) }, }, { diff --git a/gnovm/stdlibs/internal/execctx/context.go b/gnovm/stdlibs/internal/execctx/context.go index d19ac7d3be8..663c3825408 100644 --- a/gnovm/stdlibs/internal/execctx/context.go +++ b/gnovm/stdlibs/internal/execctx/context.go @@ -22,7 +22,8 @@ type ParamsInterface interface { SetUint64(key string, val uint64) SetBytes(key string, val []byte) SetStrings(key string, val []string) - UpdateStrings(key string, val []string, add bool) + AddUniqueStrings(key string, val []string) + RemoveStrings(key string, val []string) } type ExecContext struct { diff --git a/gnovm/stdlibs/sys/params/params.gno b/gnovm/stdlibs/sys/params/params.gno index a5494b94465..978bf43ce45 100644 --- a/gnovm/stdlibs/sys/params/params.gno +++ b/gnovm/stdlibs/sys/params/params.gno @@ -31,11 +31,18 @@ func SetSysParamStrings(module, submodule, name string, val []string) { setSysParamStrings(module, submodule, name, val) } -func UpdateSysParamStrings(module, submodule, name string, val []string, add bool) { +func AddUniqueSysParamStrings(module, submodule, name string, val []string) { if val == nil { val = []string{} } - updateSysParamStrings(module, submodule, name, val, add) + addUniqueSysParamStrings(module, submodule, name, val) +} + +func RemoveSysParamStrings(module, submodule, name string, val []string) { + if val == nil { + val = []string{} + } + removeSysParamStrings(module, submodule, name, val) } func setSysParamString(module, submodule, name string, val string) @@ -44,4 +51,5 @@ func setSysParamInt64(module, submodule, name string, val int64) func setSysParamUint64(module, submodule, name string, val uint64) func setSysParamBytes(module, submodule, name string, val []byte) func setSysParamStrings(module, submodule, name string, val []string) -func updateSysParamStrings(module, submodule, name string, val []string, add bool) +func addUniqueSysParamStrings(module, submodule, name string, val []string) +func removeSysParamStrings(module, submodule, name string, val []string) diff --git a/gnovm/stdlibs/sys/params/params.go b/gnovm/stdlibs/sys/params/params.go index 5fcdc045c22..38f8b412449 100644 --- a/gnovm/stdlibs/sys/params/params.go +++ b/gnovm/stdlibs/sys/params/params.go @@ -43,10 +43,16 @@ func X_setSysParamStrings(m *gno.Machine, module, submodule, name string, val [] execctx.GetContext(m).Params.SetStrings(pk, val) } -func X_updateSysParamStrings(m *gno.Machine, module, submodule, name string, val []string, add bool) { +func X_addUniqueSysParamStrings(m *gno.Machine, module, submodule, name string, val []string) { assertSysParamsRealm(m) pk := prmkey(module, submodule, name) - execctx.GetContext(m).Params.UpdateStrings(pk, val, add) + execctx.GetContext(m).Params.AddUniqueStrings(pk, val) +} + +func X_removeSysParamStrings(m *gno.Machine, module, submodule, name string, val []string) { + assertSysParamsRealm(m) + pk := prmkey(module, submodule, name) + execctx.GetContext(m).Params.RemoveStrings(pk, val) } func assertSysParamsRealm(m *gno.Machine) { diff --git a/tm2/pkg/sdk/auth/params.go b/tm2/pkg/sdk/auth/params.go index 25593722e83..8da14d2113b 100644 --- a/tm2/pkg/sdk/auth/params.go +++ b/tm2/pkg/sdk/auth/params.go @@ -160,59 +160,48 @@ func (ak AccountKeeper) WillSetParam(ctx sdk.Context, key string, value any) { if !ok { return } - ak.applyUnrestrictedAddrsChange(ctx, addrs) + ak.updateUnrestrictedAddrs(ctx, addrs, true) default: // No-op for unrecognized keys logger.Error("No-op for unrecognized keys", "key", key) } } -func (ak AccountKeeper) applyUnrestrictedAddrsChange(ctx sdk.Context, newAddrs []string) { - params, ok := ctx.Value(AuthParamsContextKey{}).(Params) - if !ok { - panic("missing or invalid AuthParams in context") - } - // Build sets once. - oldSet := make(map[string]struct{}, len(params.UnrestrictedAddrs)) - for _, addr := range params.UnrestrictedAddrs { - oldSet[addr.String()] = struct{}{} - } - newSet := make(map[string]struct{}, len(newAddrs)) - for _, s := range newAddrs { - newSet[s] = struct{}{} - } - // addition - for s := range newSet { - if _, ok := oldSet[s]; ok { - continue // in both, no change - } - addr, err := crypto.AddressFromString(s) - if err != nil { - panic(fmt.Sprintf("invalid address: %v", err)) - } - acc := ak.GetAccount(ctx, addr) - uacc, ok := acc.(std.AccountUnrestricter) +func (ak AccountKeeper) WillUpdateParam(ctx sdk.Context, key string, value any, add bool) { + switch key { + case "p:unrestricted_addrs": + addrs, ok := value.([]string) if !ok { - continue + return } - uacc.SetTokenLockWhitelisted(true) - ak.SetAccount(ctx, acc) + ak.updateUnrestrictedAddrs(ctx, addrs, add) + default: + panic("XXX") } - // removal - for s := range oldSet { - if _, ok := newSet[s]; ok { - continue // in both, no change - } +} + +func (ak AccountKeeper) updateUnrestrictedAddrs(ctx sdk.Context, addrs []string, whitelist bool) { + for _, s := range addrs { addr, err := crypto.AddressFromString(s) if err != nil { panic(fmt.Sprintf("invalid address: %v", err)) } acc := ak.GetAccount(ctx, addr) + if acc == nil { + continue + } + uacc, ok := acc.(std.AccountUnrestricter) if !ok { continue } - uacc.SetTokenLockWhitelisted(false) + + if uacc.IsTokenLockWhitelisted() == whitelist { + continue + } + + uacc.SetTokenLockWhitelisted(whitelist) + ak.SetAccount(ctx, acc) } } diff --git a/tm2/pkg/sdk/auth/params_test.go b/tm2/pkg/sdk/auth/params_test.go index 2370234b5f0..27b11e08f46 100644 --- a/tm2/pkg/sdk/auth/params_test.go +++ b/tm2/pkg/sdk/auth/params_test.go @@ -132,3 +132,51 @@ func TestParamsString(t *testing.T) { }) } } + +func TestSetParams(t *testing.T) { + // Define expected values for each parameter + maxMemoBytes := int64(256) + txSigLimit := int64(10) + txSizeCostPerByte := int64(5) + sigVerifyCostED25519 := int64(100) + sigVerifyCostSecp256k1 := int64(200) + gasPricesChangeCompressor := int64(50) + targetGasRatio := int64(75) + feeCollector := crypto.AddressFromPreimage([]byte("test_collector")) + + // Call NewParams with the values + params := NewParams( + maxMemoBytes, + txSigLimit, + txSizeCostPerByte, + sigVerifyCostED25519, + sigVerifyCostSecp256k1, + gasPricesChangeCompressor, + targetGasRatio, + feeCollector, + ) + + // Create an expected Params struct with the same values + expectedParams := Params{ + MaxMemoBytes: maxMemoBytes, + TxSigLimit: txSigLimit, + TxSizeCostPerByte: txSizeCostPerByte, + SigVerifyCostED25519: sigVerifyCostED25519, + SigVerifyCostSecp256k1: sigVerifyCostSecp256k1, + GasPricesChangeCompressor: gasPricesChangeCompressor, + TargetGasRatio: targetGasRatio, + FeeCollector: feeCollector, + } + + // Check if the returned params struct matches the expected struct + if !reflect.DeepEqual(params, expectedParams) { + t.Errorf("NewParams() = %+v, want %+v", params, expectedParams) + } + + // set params + env := setupTestEnv() + env.acck.SetParams(env.ctx, params) + + params2 := env.acck.GetParams(env.ctx) + assert.Equal(t, params, params2, "params got not equal") +} diff --git a/tm2/pkg/sdk/auth/test_common.go b/tm2/pkg/sdk/auth/test_common.go index dfc67de061c..b10a1c5d988 100644 --- a/tm2/pkg/sdk/auth/test_common.go +++ b/tm2/pkg/sdk/auth/test_common.go @@ -99,4 +99,5 @@ func (bankk DummyBankKeeper) SendCoins(ctx sdk.Context, fromAddr crypto.Address, } // WillSetParam checks if the key contains the module's parameter key prefix and updates the module parameter accordingly. -func (bankk DummyBankKeeper) WillSetParam(ctx sdk.Context, key string, value any) {} +func (bankk DummyBankKeeper) WillSetParam(ctx sdk.Context, key string, value any) {} +func (bankk DummyBankKeeper) WillUpdateParam(ctx sdk.Context, key string, value any, add bool) {} diff --git a/tm2/pkg/sdk/bank/params.go b/tm2/pkg/sdk/bank/params.go index ac41a4b2c6e..a376f345c26 100644 --- a/tm2/pkg/sdk/bank/params.go +++ b/tm2/pkg/sdk/bank/params.go @@ -67,3 +67,7 @@ func (bank BankKeeper) WillSetParam(ctx sdk.Context, key string, value any) { // Allow setting non-existent key. } } + +func (bank BankKeeper) WillUpdateParam(ctx sdk.Context, key string, value any, add bool) { + // XXX +} diff --git a/tm2/pkg/sdk/params/keeper.go b/tm2/pkg/sdk/params/keeper.go index 91d435738fb..71bd5a9071a 100644 --- a/tm2/pkg/sdk/params/keeper.go +++ b/tm2/pkg/sdk/params/keeper.go @@ -39,6 +39,9 @@ type ParamsKeeperI interface { SetBytes(ctx sdk.Context, key string, value []byte) SetStrings(ctx sdk.Context, key string, value []string) + AddUniqueStrings(ctx sdk.Context, key string, delta []string) + RemoveStrings(ctx sdk.Context, key string, delta []string) + Has(ctx sdk.Context, key string) bool GetRaw(ctx sdk.Context, key string) []byte SetRaw(ctx sdk.Context, key string, value []byte) @@ -53,6 +56,7 @@ type ParamsKeeperI interface { type ParamfulKeeper interface { WillSetParam(ctx sdk.Context, key string, value any) + WillUpdateParam(ctx sdk.Context, key string, value any, add bool) } var _ ParamsKeeperI = ParamsKeeper{} @@ -158,6 +162,101 @@ func (pk ParamsKeeper) SetStrings(ctx sdk.Context, key string, value []string) { pk.set(ctx, key, value) } +func (pk ParamsKeeper) RemoveStrings(ctx sdk.Context, key string, values []string) { + ss := &[]string{} + pk.GetStrings(ctx, key, ss) + + if len(values) == 0 { + return + } + + oldList := *ss + changed := false + + if len(oldList) == 0 { + return + } + + // Build the set of items to remove + removeSet := make(map[string]struct{}, len(values)) + for _, v := range values { + removeSet[v] = struct{}{} + } + + // Filter in-place (reuse memory) + // n is the index for the "kept" items + n := 0 + for _, s := range oldList { + if _, found := removeSet[s]; !found { + oldList[n] = s + n++ + } else { + changed = true + } + } + + // Reslice to the new length + oldList = oldList[:n] + + if changed { + *ss = oldList // Update the pointer + + // update paramful keeper + module, rawKey := parsePrefix(key) + if module != "" { + kpr := pk.GetRegisteredKeeper(module) + if kpr != nil { + add := false + kpr.WillUpdateParam(ctx, rawKey, *ss, add) + } + } + + pk.SetStrings(ctx, key, oldList) // Write to Store + } +} + +func (pk ParamsKeeper) AddUniqueStrings(ctx sdk.Context, key string, values []string) { + ss := &[]string{} + pk.GetStrings(ctx, key, ss) + + if len(values) == 0 { + return + } + + oldList := *ss + changed := false + + // Use a map to track existing items to prevent duplicates. + exists := make(map[string]struct{}, len(oldList)+len(values)) + for _, s := range oldList { + exists[s] = struct{}{} + } + + for _, v := range values { + if _, found := exists[v]; !found { + oldList = append(oldList, v) + exists[v] = struct{}{} // Mark found so we don't add duplicates from delta itself + changed = true + } + } + + if changed { + *ss = oldList // Update the pointer + + // update paramful keeper + module, rawKey := parsePrefix(key) + if module != "" { + kpr := pk.GetRegisteredKeeper(module) + if kpr != nil { + add := true + kpr.WillUpdateParam(ctx, rawKey, *ss, add) + } + } + + pk.SetStrings(ctx, key, oldList) // Write to Store + } +} + func (pk ParamsKeeper) GetRaw(ctx sdk.Context, key string) []byte { stor := ctx.Store(pk.key) return stor.Get(storeKey(key)) @@ -331,6 +430,14 @@ func (ppk prefixParamsKeeper) SetStrings(ctx sdk.Context, key string, value []st ppk.pk.SetStrings(ctx, ppk.prefixed(key), value) } +func (ppk prefixParamsKeeper) AddUniqueStrings(ctx sdk.Context, key string, value []string) { + ppk.pk.AddUniqueStrings(ctx, ppk.prefixed(key), value) +} + +func (ppk prefixParamsKeeper) RemoveStrings(ctx sdk.Context, key string, value []string) { + ppk.pk.RemoveStrings(ctx, ppk.prefixed(key), value) +} + func (ppk prefixParamsKeeper) Has(ctx sdk.Context, key string) bool { return ppk.pk.Has(ctx, ppk.prefixed(key)) } diff --git a/tm2/pkg/sdk/params/test_common.go b/tm2/pkg/sdk/params/test_common.go index e81743af120..9312fdf79c7 100644 --- a/tm2/pkg/sdk/params/test_common.go +++ b/tm2/pkg/sdk/params/test_common.go @@ -62,3 +62,6 @@ func NewDummyKeeper(prmk ParamsKeeperI) DummyKeeper { func (dk DummyKeeper) WillSetParam(ctx sdk.Context, key string, value any) { // do nothing } +func (dk DummyKeeper) WillUpdateParam(ctx sdk.Context, key string, value any, add bool) { + // do nothing +}