From c868357eca8f10ea8014a864cd226ab55e15425b Mon Sep 17 00:00:00 2001 From: Til Jordan Date: Tue, 23 Sep 2025 18:23:42 +0000 Subject: [PATCH 1/8] wasm: add THOR_WASM_PERMISSION_MODE env to select DefaultPermissionKeeper for permissionless mode --- x/thorchain/manager_wasm_current.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/x/thorchain/manager_wasm_current.go b/x/thorchain/manager_wasm_current.go index 0b785f98e..ec310cac2 100644 --- a/x/thorchain/manager_wasm_current.go +++ b/x/thorchain/manager_wasm_current.go @@ -3,6 +3,7 @@ package thorchain import ( "encoding/base32" "strings" + "os" wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" @@ -338,6 +339,9 @@ func (m WasmMgrVCUR) checkChecksumHalt(ctx cosmos.Context, checksum []byte) erro } func (m WasmMgrVCUR) permissionedKeeper() *wasmkeeper.PermissionedKeeper { + if mode := os.Getenv("THOR_WASM_PERMISSION_MODE"); mode == "permissionless" { + return wasmkeeper.NewDefaultPermissionKeeper(m.wasmKeeper) + } return wasmkeeper.NewGovPermissionKeeper(m.wasmKeeper) } From cfb22fc28ae3cee2025f12335ecaf39c1287c84e Mon Sep 17 00:00:00 2001 From: Til Jordan Date: Tue, 23 Sep 2025 18:34:04 +0000 Subject: [PATCH 2/8] wasm: allow store/instantiate when THOR_WASM_PERMISSION_MODE=permissionless regardless of Mimir --- x/thorchain/manager_wasm_current.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/x/thorchain/manager_wasm_current.go b/x/thorchain/manager_wasm_current.go index ec310cac2..30ac4caac 100644 --- a/x/thorchain/manager_wasm_current.go +++ b/x/thorchain/manager_wasm_current.go @@ -346,6 +346,9 @@ func (m WasmMgrVCUR) permissionedKeeper() *wasmkeeper.PermissionedKeeper { } func (m WasmMgrVCUR) checkCanStore(ctx cosmos.Context, actor cosmos.AccAddress) error { + if mode := os.Getenv("THOR_WASM_PERMISSION_MODE"); mode == "permissionless" { + return nil + } err := m.checkActor(ctx, actor) if err != nil { return err @@ -363,6 +366,9 @@ func (m WasmMgrVCUR) checkCanStore(ctx cosmos.Context, actor cosmos.AccAddress) } func (m WasmMgrVCUR) checkCanInstantiate(ctx cosmos.Context, actor cosmos.AccAddress) error { + if mode := os.Getenv("THOR_WASM_PERMISSION_MODE"); mode == "permissionless" { + return nil + } err := m.checkActor(ctx, actor) if err != nil { return err From 66a68c4720ce7dd34ddedeb1bbfb7b240735946e Mon Sep 17 00:00:00 2001 From: Til Jordan Date: Tue, 23 Sep 2025 19:16:52 +0000 Subject: [PATCH 3/8] wasm: cache normalized THOR_WASM_PERMISSION_MODE at init; reuse in permission checks --- x/thorchain/manager_wasm_current.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/x/thorchain/manager_wasm_current.go b/x/thorchain/manager_wasm_current.go index 30ac4caac..e3457cb36 100644 --- a/x/thorchain/manager_wasm_current.go +++ b/x/thorchain/manager_wasm_current.go @@ -18,6 +18,14 @@ import ( "gitlab.com/thorchain/thornode/v3/common/wasmpermissions" ) +var wasmPermMode = func() string { + m := strings.ToLower(strings.TrimSpace(os.Getenv("THOR_WASM_PERMISSION_MODE"))) + if m == "permissionless" { + return "permissionless" + } + return "" +}() + var _ WasmManager = &WasmMgrVCUR{} // WasmMgrVCUR is VCUR implementation of slasher @@ -339,14 +347,14 @@ func (m WasmMgrVCUR) checkChecksumHalt(ctx cosmos.Context, checksum []byte) erro } func (m WasmMgrVCUR) permissionedKeeper() *wasmkeeper.PermissionedKeeper { - if mode := os.Getenv("THOR_WASM_PERMISSION_MODE"); mode == "permissionless" { + if wasmPermMode == "permissionless" { return wasmkeeper.NewDefaultPermissionKeeper(m.wasmKeeper) } return wasmkeeper.NewGovPermissionKeeper(m.wasmKeeper) } func (m WasmMgrVCUR) checkCanStore(ctx cosmos.Context, actor cosmos.AccAddress) error { - if mode := os.Getenv("THOR_WASM_PERMISSION_MODE"); mode == "permissionless" { + if wasmPermMode == "permissionless" { return nil } err := m.checkActor(ctx, actor) @@ -366,7 +374,7 @@ func (m WasmMgrVCUR) checkCanStore(ctx cosmos.Context, actor cosmos.AccAddress) } func (m WasmMgrVCUR) checkCanInstantiate(ctx cosmos.Context, actor cosmos.AccAddress) error { - if mode := os.Getenv("THOR_WASM_PERMISSION_MODE"); mode == "permissionless" { + if wasmPermMode == "permissionless" { return nil } err := m.checkActor(ctx, actor) From a1a271565f4352ba1b53ed5a852b42e9181bc086 Mon Sep 17 00:00:00 2001 From: Til Jordan Date: Tue, 23 Sep 2025 21:10:29 +0000 Subject: [PATCH 4/8] bloctopus: add permissionless WasmManager and env-based selector; restore default wasm manager behavior --- x/bloctopus/wasm_manager.go | 112 ++++++++++++++++++++++++++++ x/thorchain/manager_wasm_current.go | 11 --- x/thorchain/managers.go | 8 ++ 3 files changed, 120 insertions(+), 11 deletions(-) create mode 100644 x/bloctopus/wasm_manager.go diff --git a/x/bloctopus/wasm_manager.go b/x/bloctopus/wasm_manager.go new file mode 100644 index 000000000..b8636acea --- /dev/null +++ b/x/bloctopus/wasm_manager.go @@ -0,0 +1,112 @@ +package bloctopus + +import ( + wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" + wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "gitlab.com/thorchain/thornode/v3/common/cosmos" + "gitlab.com/thorchain/thornode/v3/x/thorchain" + "gitlab.com/thorchain/thornode/v3/x/thorchain/keeper" +) + +var _ thorchain.WasmManager = &WasmMgrPermissionless{} + +type WasmMgrPermissionless struct { + keeper keeper.Keeper + wasmKeeper wasmkeeper.Keeper + eventMgr thorchain.EventManager +} + +func NewWasmMgrPermissionless(k keeper.Keeper, wk wasmkeeper.Keeper, ev thorchain.EventManager) (*WasmMgrPermissionless, error) { + return &WasmMgrPermissionless{ + keeper: k, + wasmKeeper: wk, + eventMgr: ev, + }, nil +} + +func (m WasmMgrPermissionless) permKeeper() *wasmkeeper.PermissionedKeeper { + return wasmkeeper.NewDefaultPermissionKeeper(m.wasmKeeper) +} + +func (m WasmMgrPermissionless) StoreCode( + ctx cosmos.Context, + creator sdk.AccAddress, + wasmCode []byte, +) (codeID uint64, checksum []byte, err error) { + return m.permKeeper().Create(ctx, creator, wasmCode, nil) +} + +func (m WasmMgrPermissionless) InstantiateContract( + ctx cosmos.Context, + codeID uint64, + creator, admin sdk.AccAddress, + initMsg []byte, + label string, + deposit sdk.Coins, +) (sdk.AccAddress, []byte, error) { + return m.permKeeper().Instantiate(ctx, codeID, creator, admin, initMsg, label, deposit) +} + +func (m WasmMgrPermissionless) InstantiateContract2( + ctx cosmos.Context, + codeID uint64, + creator, admin sdk.AccAddress, + initMsg []byte, + label string, + deposit sdk.Coins, + salt []byte, + fixMsg bool, +) (sdk.AccAddress, []byte, error) { + return m.permKeeper().Instantiate2(ctx, codeID, creator, admin, initMsg, label, deposit, salt, fixMsg) +} + +func (m WasmMgrPermissionless) ExecuteContract( + ctx cosmos.Context, + contractAddr, senderAddr sdk.AccAddress, + msg []byte, + coins sdk.Coins, +) ([]byte, error) { + return m.permKeeper().Execute(ctx, contractAddr, senderAddr, msg, coins) +} + +func (m WasmMgrPermissionless) MigrateContract( + ctx cosmos.Context, + contractAddress, caller sdk.AccAddress, + newCodeID uint64, + msg []byte, +) ([]byte, error) { + return m.permKeeper().Migrate(ctx, contractAddress, caller, newCodeID, msg) +} + +func (m WasmMgrPermissionless) SudoContract( + ctx cosmos.Context, + contractAddress, _ sdk.AccAddress, + msg []byte, +) ([]byte, error) { + return m.permKeeper().Sudo(ctx, contractAddress, msg) +} + +func (m WasmMgrPermissionless) UpdateAdmin( + ctx cosmos.Context, + contractAddress, sender, newAdmin sdk.AccAddress, +) ([]byte, error) { + return nil, m.permKeeper().UpdateContractAdmin(ctx, contractAddress, sender, newAdmin) +} + +func (m WasmMgrPermissionless) ClearAdmin( + ctx cosmos.Context, + contractAddress, sender sdk.AccAddress, +) ([]byte, error) { + return nil, m.permKeeper().ClearContractAdmin(ctx, contractAddress, sender) +} + +func (m WasmMgrPermissionless) getCodeInfo(ctx cosmos.Context, id uint64) (*wasmtypes.CodeInfo, error) { + codeInfo := m.wasmKeeper.GetCodeInfo(ctx, id) + if codeInfo == nil { + return nil, wasmtypes.ErrNotFound + } + return codeInfo, nil +} diff --git a/x/thorchain/manager_wasm_current.go b/x/thorchain/manager_wasm_current.go index e3457cb36..955a4d0de 100644 --- a/x/thorchain/manager_wasm_current.go +++ b/x/thorchain/manager_wasm_current.go @@ -2,8 +2,6 @@ package thorchain import ( "encoding/base32" - "strings" - "os" wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" @@ -347,16 +345,10 @@ func (m WasmMgrVCUR) checkChecksumHalt(ctx cosmos.Context, checksum []byte) erro } func (m WasmMgrVCUR) permissionedKeeper() *wasmkeeper.PermissionedKeeper { - if wasmPermMode == "permissionless" { - return wasmkeeper.NewDefaultPermissionKeeper(m.wasmKeeper) - } return wasmkeeper.NewGovPermissionKeeper(m.wasmKeeper) } func (m WasmMgrVCUR) checkCanStore(ctx cosmos.Context, actor cosmos.AccAddress) error { - if wasmPermMode == "permissionless" { - return nil - } err := m.checkActor(ctx, actor) if err != nil { return err @@ -374,9 +366,6 @@ func (m WasmMgrVCUR) checkCanStore(ctx cosmos.Context, actor cosmos.AccAddress) } func (m WasmMgrVCUR) checkCanInstantiate(ctx cosmos.Context, actor cosmos.AccAddress) error { - if wasmPermMode == "permissionless" { - return nil - } err := m.checkActor(ctx, actor) if err != nil { return err diff --git a/x/thorchain/managers.go b/x/thorchain/managers.go index c4f7dfcd1..60521abab 100644 --- a/x/thorchain/managers.go +++ b/x/thorchain/managers.go @@ -3,6 +3,9 @@ package thorchain import ( "errors" "fmt" + "os" + "strings" + "cosmossdk.io/core/store" upgradekeeper "cosmossdk.io/x/upgrade/keeper" @@ -21,6 +24,8 @@ import ( "gitlab.com/thorchain/thornode/v3/x/thorchain/keeper" kv1 "gitlab.com/thorchain/thornode/v3/x/thorchain/keeper/v1" "gitlab.com/thorchain/thornode/v3/x/thorchain/types" + "gitlab.com/thorchain/thornode/v3/x/bloctopus" + ) const ( @@ -587,6 +592,9 @@ func GetSecuredAssetManager(version semver.Version, keeper keeper.Keeper, eventM } func GetWasmManager(ctx cosmos.Context, keeper keeper.Keeper, wasmKeeper wasmkeeper.Keeper, eventMgr EventManager) (WasmManager, error) { + if strings.EqualFold(strings.TrimSpace(os.Getenv("THOR_WASM_MANAGER")), "bloctopus") { + return bloctopus.NewWasmMgrPermissionless(keeper, wasmKeeper, eventMgr) + } return newWasmMgrVCUR(keeper, wasmKeeper, wasmpermissions.GetWasmPermissions(), eventMgr) } From 342917b1cc147adacd5758606191de8950972f1b Mon Sep 17 00:00:00 2001 From: Til Jordan Date: Tue, 23 Sep 2025 21:12:26 +0000 Subject: [PATCH 5/8] bloctopus: remove thorchain dependency to avoid import cycle; adjust constructor and selector --- x/bloctopus/wasm_manager.go | 7 +------ x/thorchain/managers.go | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/x/bloctopus/wasm_manager.go b/x/bloctopus/wasm_manager.go index b8636acea..be9b5f913 100644 --- a/x/bloctopus/wasm_manager.go +++ b/x/bloctopus/wasm_manager.go @@ -7,23 +7,18 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "gitlab.com/thorchain/thornode/v3/common/cosmos" - "gitlab.com/thorchain/thornode/v3/x/thorchain" "gitlab.com/thorchain/thornode/v3/x/thorchain/keeper" ) -var _ thorchain.WasmManager = &WasmMgrPermissionless{} - type WasmMgrPermissionless struct { keeper keeper.Keeper wasmKeeper wasmkeeper.Keeper - eventMgr thorchain.EventManager } -func NewWasmMgrPermissionless(k keeper.Keeper, wk wasmkeeper.Keeper, ev thorchain.EventManager) (*WasmMgrPermissionless, error) { +func NewWasmMgrPermissionless(k keeper.Keeper, wk wasmkeeper.Keeper) (*WasmMgrPermissionless, error) { return &WasmMgrPermissionless{ keeper: k, wasmKeeper: wk, - eventMgr: ev, }, nil } diff --git a/x/thorchain/managers.go b/x/thorchain/managers.go index 60521abab..95471ad83 100644 --- a/x/thorchain/managers.go +++ b/x/thorchain/managers.go @@ -593,7 +593,7 @@ func GetSecuredAssetManager(version semver.Version, keeper keeper.Keeper, eventM func GetWasmManager(ctx cosmos.Context, keeper keeper.Keeper, wasmKeeper wasmkeeper.Keeper, eventMgr EventManager) (WasmManager, error) { if strings.EqualFold(strings.TrimSpace(os.Getenv("THOR_WASM_MANAGER")), "bloctopus") { - return bloctopus.NewWasmMgrPermissionless(keeper, wasmKeeper, eventMgr) + return bloctopus.NewWasmMgrPermissionless(keeper, wasmKeeper) } return newWasmMgrVCUR(keeper, wasmKeeper, wasmpermissions.GetWasmPermissions(), eventMgr) } From ef6e8eaa7c56ab29edbb836dcdb43f8374a6a88c Mon Sep 17 00:00:00 2001 From: Til Jordan Date: Tue, 23 Sep 2025 21:14:22 +0000 Subject: [PATCH 6/8] revert env-based wasm permission mode remnants; remove strings/os usage --- x/thorchain/manager_wasm_current.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/x/thorchain/manager_wasm_current.go b/x/thorchain/manager_wasm_current.go index 955a4d0de..8de310c11 100644 --- a/x/thorchain/manager_wasm_current.go +++ b/x/thorchain/manager_wasm_current.go @@ -16,13 +16,6 @@ import ( "gitlab.com/thorchain/thornode/v3/common/wasmpermissions" ) -var wasmPermMode = func() string { - m := strings.ToLower(strings.TrimSpace(os.Getenv("THOR_WASM_PERMISSION_MODE"))) - if m == "permissionless" { - return "permissionless" - } - return "" -}() var _ WasmManager = &WasmMgrVCUR{} From 32797368c27cf997ba54ed67882b10028c376141 Mon Sep 17 00:00:00 2001 From: Til Jordan Date: Tue, 23 Sep 2025 21:16:08 +0000 Subject: [PATCH 7/8] thorchain: add missing strings import after reverting env-based wasm perm logic --- x/thorchain/manager_wasm_current.go | 1 + 1 file changed, 1 insertion(+) diff --git a/x/thorchain/manager_wasm_current.go b/x/thorchain/manager_wasm_current.go index 8de310c11..bd897c2cb 100644 --- a/x/thorchain/manager_wasm_current.go +++ b/x/thorchain/manager_wasm_current.go @@ -2,6 +2,7 @@ package thorchain import ( "encoding/base32" + "strings" wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" From 4e4d6414862464d7367ca49cdaa58628e56766b2 Mon Sep 17 00:00:00 2001 From: Til Jordan Date: Wed, 24 Sep 2025 13:32:02 +0000 Subject: [PATCH 8/8] bloctopus: add maybePin/maybeUnpin and call around instantiate/migrate for parity with default manager --- x/bloctopus/wasm_manager.go | 49 ++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/x/bloctopus/wasm_manager.go b/x/bloctopus/wasm_manager.go index be9b5f913..d82a0709e 100644 --- a/x/bloctopus/wasm_manager.go +++ b/x/bloctopus/wasm_manager.go @@ -42,6 +42,9 @@ func (m WasmMgrPermissionless) InstantiateContract( label string, deposit sdk.Coins, ) (sdk.AccAddress, []byte, error) { + if err := m.maybePin(ctx, codeID); err != nil { + return nil, nil, err + } return m.permKeeper().Instantiate(ctx, codeID, creator, admin, initMsg, label, deposit) } @@ -55,6 +58,9 @@ func (m WasmMgrPermissionless) InstantiateContract2( salt []byte, fixMsg bool, ) (sdk.AccAddress, []byte, error) { + if err := m.maybePin(ctx, codeID); err != nil { + return nil, nil, err + } return m.permKeeper().Instantiate2(ctx, codeID, creator, admin, initMsg, label, deposit, salt, fixMsg) } @@ -73,7 +79,20 @@ func (m WasmMgrPermissionless) MigrateContract( newCodeID uint64, msg []byte, ) ([]byte, error) { - return m.permKeeper().Migrate(ctx, contractAddress, caller, newCodeID, msg) + if err := m.maybePin(ctx, newCodeID); err != nil { + return nil, err + } + data, err := m.permKeeper().Migrate(ctx, contractAddress, caller, newCodeID, msg) + if err != nil { + return nil, err + } + contractInfo := m.wasmKeeper.GetContractInfo(ctx, contractAddress) + if contractInfo != nil { + if err := m.maybeUnpin(ctx, contractInfo.CodeID); err != nil { + return nil, err + } + } + return data, nil } func (m WasmMgrPermissionless) SudoContract( @@ -105,3 +124,31 @@ func (m WasmMgrPermissionless) getCodeInfo(ctx cosmos.Context, id uint64) (*wasm } return codeInfo, nil } + +func (m WasmMgrPermissionless) maybePin(ctx cosmos.Context, codeId uint64) error { + var instanceCount int + m.wasmKeeper.IterateContractsByCode(ctx, codeId, func(address sdk.AccAddress) bool { + instanceCount++ + return true + }) + if instanceCount == 0 { + if err := m.permKeeper().PinCode(ctx, codeId); err != nil { + return err + } + } + return nil +} + +func (m WasmMgrPermissionless) maybeUnpin(ctx cosmos.Context, codeId uint64) error { + var instanceCount int + m.wasmKeeper.IterateContractsByCode(ctx, codeId, func(address sdk.AccAddress) bool { + instanceCount++ + return true + }) + if instanceCount == 0 { + if err := m.permKeeper().UnpinCode(ctx, codeId); err != nil { + return err + } + } + return nil +}