diff --git a/CHANGELOG.md b/CHANGELOG.md index 5bda71ba9..968baa60a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * Remove the warnings about some config settings [2095](https://github.com/provenance-io/provenance/pull/2095). * Record several scope NAVs with the umber upgrade [#2085](https://github.com/provenance-io/provenance/pull/2085). +* Store the Figure Funding Trading Bridge Smart Contract as part of the umber upgrade [2102](https://github.com/provenance-io/provenance/pull/2102). ### Dependencies diff --git a/app/upgrade_files/umber/funding_trading_bridge_smart_contract.wasm b/app/upgrade_files/umber/funding_trading_bridge_smart_contract.wasm new file mode 100644 index 000000000..1a6e49ded Binary files /dev/null and b/app/upgrade_files/umber/funding_trading_bridge_smart_contract.wasm differ diff --git a/app/upgrades.go b/app/upgrades.go index 79ccf03e4..2b3b1b16c 100644 --- a/app/upgrades.go +++ b/app/upgrades.go @@ -5,6 +5,8 @@ import ( "fmt" "time" + wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" + wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" "github.com/google/uuid" sdkmath "cosmossdk.io/math" @@ -154,6 +156,8 @@ var upgrades = map[string]appUpgrade{ removeInactiveValidatorDelegations(ctx, app) + storeWasmCode(ctx, app) + return vm, nil }, }, @@ -623,3 +627,45 @@ func addScopeNAVsWithHeight(ctx sdk.Context, app *App, scopeNAVs []ScopeNAV) { ctx.Logger().Info(fmt.Sprintf("Successfully added %d of %d scope net asset value entries.", totalAdded, count)) } + +// storeWasmCode will store the provided wasm contract. +// TODO: Remove with the umber handlers. +func storeWasmCode(ctx sdk.Context, app *App) { + ctx.Logger().Info("Storing the Funding Trading Bridge smart contract.") + defer func() { + ctx.Logger().Info("Done storing the Funding Trading Bridge smart contract.") + }() + + codeBz, err := UpgradeFiles.ReadFile("upgrade_files/umber/funding_trading_bridge_smart_contract.wasm") + if err != nil { + ctx.Logger().Error("Could not read smart contract.", "error", err) + return + } + + msg := &wasmtypes.MsgStoreCode{ + Sender: app.GovKeeper.GetAuthority(), + WASMByteCode: codeBz, + InstantiatePermission: &wasmtypes.AccessConfig{Permission: wasmtypes.AccessTypeEverybody}, + } + executeStoreCodeMsg(ctx, wasmkeeper.NewMsgServerImpl(app.WasmKeeper), msg) +} + +// wasmMsgSrvr has just the StoreCode endpoint needed for this upgrade. +// TODO: Remove with the umber handlers. +type wasmMsgSrvr interface { + StoreCode(context.Context, *wasmtypes.MsgStoreCode) (*wasmtypes.MsgStoreCodeResponse, error) +} + +// executeStoreCodeMsg executes a MsgStoreCode. +// TODO: Remove with the umber handlers. +func executeStoreCodeMsg(ctx sdk.Context, wasmMsgServer wasmMsgSrvr, msg *wasmtypes.MsgStoreCode) { + cacheCtx, writeCache := ctx.CacheContext() + resp, err := wasmMsgServer.StoreCode(cacheCtx, msg) + if err != nil { + ctx.Logger().Error("Could not store smart contract.", "error", err) + return + } + writeCache() + ctx.Logger().Info(fmt.Sprintf("Smart contract stored with codeID: %d and checksum: %q.", + resp.CodeID, fmt.Sprintf("%x", resp.Checksum))) +} diff --git a/app/upgrades_test.go b/app/upgrades_test.go index 7c4961171..b8f01d74e 100644 --- a/app/upgrades_test.go +++ b/app/upgrades_test.go @@ -2,6 +2,9 @@ package app import ( "bytes" + "context" + "embed" + "errors" "fmt" "regexp" "sort" @@ -9,6 +12,8 @@ import ( "testing" "time" + wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" + wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" "github.com/google/uuid" "github.com/stretchr/testify/suite" @@ -446,6 +451,8 @@ func (s *UpgradeTestSuite) TestUmber() { "INF Removing inactive validator delegations.", "INF Threshold: 21 days", "INF A total of 0 inactive (unbonded) validators have had all their delegators removed.", + "INF Storing the Funding Trading Bridge smart contract.", + "INF Done storing the Funding Trading Bridge smart contract.", } s.AssertUpgradeHandlerLogs("umber", expInLog, nil) @@ -909,3 +916,109 @@ func (s *UpgradeTestSuite) TestAddScopeNAVsWithHeight() { }) } } + +// wrappedWasmMsgSrvr is a wasmtypes.MsgServer that lets us inject an error to return from StoreCode. +type wrappedWasmMsgSrvr struct { + wasmMsgServer wasmtypes.MsgServer + storeCodeErr string +} + +func newWrappedWasmMsgSrvr(wasmKeeper *wasmkeeper.Keeper) *wrappedWasmMsgSrvr { + return &wrappedWasmMsgSrvr{wasmMsgServer: wasmkeeper.NewMsgServerImpl(wasmKeeper)} +} + +func (w *wrappedWasmMsgSrvr) WithStoreCodeErr(str string) *wrappedWasmMsgSrvr { + w.storeCodeErr = str + return w +} + +func (w *wrappedWasmMsgSrvr) StoreCode(ctx context.Context, msg *wasmtypes.MsgStoreCode) (*wasmtypes.MsgStoreCodeResponse, error) { + if len(w.storeCodeErr) > 0 { + return nil, errors.New(w.storeCodeErr) + } + return w.wasmMsgServer.StoreCode(ctx, msg) +} + +func (s *UpgradeTestSuite) TestStoreWasmCode() { + origUpgradeFiles := UpgradeFiles + defer func() { + UpgradeFiles = origUpgradeFiles + }() + + tests := []struct { + name string + upgradeFiles embed.FS + expLogs []string + }{ + { + name: "success", + upgradeFiles: UpgradeFiles, + expLogs: []string{ + "INF Storing the Funding Trading Bridge smart contract.", + "INF Smart contract stored with codeID: 1 and checksum: \"7e643e228169980aff5d75d576873d34b368d30a154dc617d2ed9b0093c97128\".", + "INF Done storing the Funding Trading Bridge smart contract.", + }, + }, + { + name: "failed to read file", + upgradeFiles: embed.FS{}, + expLogs: []string{ + "INF Storing the Funding Trading Bridge smart contract.", + "ERR Could not read smart contract. error=\"open upgrade_files/umber/funding_trading_bridge_smart_contract.wasm: file does not exist\"", + "INF Done storing the Funding Trading Bridge smart contract.", + }, + }, + } + + for _, tc := range tests { + s.Run(tc.name, func() { + UpgradeFiles = tc.upgradeFiles + s.logBuffer.Reset() + testFunc := func() { + storeWasmCode(s.ctx, s.app) + } + s.Require().NotPanics(testFunc, "storeWasmCode") + actLogs := splitLogOutput(s.GetLogOutput("executeStoreCodeMsg")) + s.Assert().Equal(tc.expLogs, actLogs, "log messages during executeStoreCodeMsg") + }) + } +} + +func (s *UpgradeTestSuite) TestExecuteStoreCodeMsg() { + codeBz, err := UpgradeFiles.ReadFile("upgrade_files/umber/funding_trading_bridge_smart_contract.wasm") + s.Require().NoError(err, "reading wasm file") + msg := &wasmtypes.MsgStoreCode{ + Sender: s.app.GovKeeper.GetAuthority(), + WASMByteCode: codeBz, + InstantiatePermission: &wasmtypes.AccessConfig{Permission: wasmtypes.AccessTypeEverybody}, + } + + tests := []struct { + name string + errMsg string + expLogs []string + }{ + { + name: "storage fails", + errMsg: "dang no worky", + expLogs: []string{"ERR Could not store smart contract. error=\"dang no worky\""}, + }, + { + name: "storage succeeds", + expLogs: []string{"INF Smart contract stored with codeID: 1 and checksum: \"7e643e228169980aff5d75d576873d34b368d30a154dc617d2ed9b0093c97128\"."}, + }, + } + + for _, tc := range tests { + s.Run(tc.name, func() { + msgServer := newWrappedWasmMsgSrvr(s.app.WasmKeeper).WithStoreCodeErr(tc.errMsg) + s.logBuffer.Reset() + testFunc := func() { + executeStoreCodeMsg(s.ctx, msgServer, msg) + } + s.Require().NotPanics(testFunc, "executeStoreCodeMsg") + actLogs := splitLogOutput(s.GetLogOutput("executeStoreCodeMsg")) + s.Assert().Equal(tc.expLogs, actLogs, "log messages during executeStoreCodeMsg") + }) + } +}