Skip to content

Commit

Permalink
Merge pull request #1676 from scrtlabs/chb19/cosmod-sdk-0.50.x-anteha…
Browse files Browse the repository at this point in the history
…ndler-mrenclave

Draft: MRENCLAVE support antehandler
  • Loading branch information
valdok authored Oct 28, 2024
2 parents af8d479 + 95271dd commit 3cf353d
Show file tree
Hide file tree
Showing 9 changed files with 661 additions and 79 deletions.
6 changes: 5 additions & 1 deletion app/ante.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ package app

import (
"cosmossdk.io/core/store"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/x/auth/ante"
"github.com/cosmos/ibc-go/v8/modules/core/keeper"

govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper"
"github.com/scrtlabs/SecretNetwork/x/compute"
)

Expand All @@ -15,6 +17,8 @@ import (
type HandlerOptions struct {
ante.HandlerOptions

appCodec codec.Codec
govkeeper govkeeper.Keeper // You'll need the keeper to access stored mrenclave hash
IBCKeeper *keeper.Keeper
WasmConfig *compute.WasmConfig
TXCounterStoreService store.KVStoreService
Expand All @@ -39,7 +43,7 @@ func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) {
}

anteDecorators := []sdk.AnteDecorator{
compute.NewCountTXDecorator(options.TXCounterStoreService),
compute.NewCountTXDecorator(options.appCodec, options.govkeeper, options.TXCounterStoreService),
ante.NewSetUpContextDecorator(), // outermost AnteDecorator. SetUpContext must be called first
ante.NewExtensionOptionsDecorator(nil),
ante.NewValidateBasicDecorator(),
Expand Down
7 changes: 7 additions & 0 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import (
"github.com/cosmos/cosmos-sdk/types/module"
vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types"
"github.com/cosmos/cosmos-sdk/x/authz"
govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper"
"github.com/cosmos/gogoproto/proto"
icatypes "github.com/cosmos/ibc-go/v8/modules/apps/27-interchain-accounts/types"
ibctransfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types"
Expand Down Expand Up @@ -175,6 +176,10 @@ func (app *SecretNetworkApp) GetIBCKeeper() *ibckeeper.Keeper {
return app.AppKeepers.IbcKeeper
}

func (app *SecretNetworkApp) GetGovKeeper() *govkeeper.Keeper {
return app.AppKeepers.GovKeeper
}

func (app *SecretNetworkApp) GetScopedIBCKeeper() capabilitykeeper.ScopedKeeper {
return app.AppKeepers.ScopedIBCKeeper
}
Expand Down Expand Up @@ -370,6 +375,8 @@ func NewSecretNetworkApp(
SignModeHandler: app.txConfig.SignModeHandler(),
SigGasConsumer: ante.DefaultSigVerificationGasConsumer,
},
appCodec: app.appCodec,
govkeeper: *app.AppKeepers.GovKeeper,
IBCKeeper: app.AppKeepers.IbcKeeper,
WasmConfig: computeConfig,
TXCounterStoreService: app.AppKeepers.ComputeKeeper.GetStoreService(),
Expand Down
15 changes: 15 additions & 0 deletions proto/secret/compute/v1beta1/msg.proto
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ service Msg {
rpc UpdateAdmin(MsgUpdateAdmin) returns (MsgUpdateAdminResponse);
// ClearAdmin removes any admin stored for a smart contract
rpc ClearAdmin(MsgClearAdmin) returns (MsgClearAdminResponse);
rpc UpgradeProposalPassed(MsgUpgradeProposalPassed) returns (MsgUpgradeProposalPassedResponse);
}

message MsgStoreCode {
Expand Down Expand Up @@ -187,3 +188,17 @@ message MsgClearAdmin {

// MsgClearAdminResponse returns empty data
message MsgClearAdminResponse {}

message MsgUpgradeProposalPassed {
option (gogoproto.goproto_getters) = false;
option (cosmos.msg.v1.signer) = "sender_address";
option (amino.name) = "wasm/MsgUpgradeProposalPassed";

// Sender is the actor that signed the message
string sender_address = 1;

// SHA256 hash of the new MREnclave
bytes mr_enclave_hash = 2;
}

message MsgUpgradeProposalPassedResponse {}
35 changes: 35 additions & 0 deletions x/compute/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ func GetTxCmd() *cobra.Command {
MigrateContractCmd(),
UpdateContractAdminCmd(),
ClearContractAdminCmd(),
UpgradeProposalPassedCmd(),
)
return txCmd
}
Expand Down Expand Up @@ -568,3 +569,37 @@ func ClearContractAdminCmd() *cobra.Command {
flags.AddTxFlagsToCmd(cmd)
return cmd
}

// UpgradeProposalPassedCmd
func UpgradeProposalPassedCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "upgrade-proposal-passed [mrenclave-hash]",
Short: "Upgrade MREnclave with a new SHA256 hash",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}

mrEnclaveHash := args[0]

// Create the message for upgrading the MREnclave
msg := types.MsgUpgradeProposalPassed{
SenderAddress: clientCtx.GetFromAddress().String(),
MrEnclaveHash: []byte(mrEnclaveHash),
}

// Validate the message
if err = msg.ValidateBasic(); err != nil {
return err
}

// Generate or broadcast the transaction
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg)
},
}
flags.AddTxFlagsToCmd(cmd)

return cmd
}
107 changes: 105 additions & 2 deletions x/compute/internal/keeper/ante.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,56 @@ package keeper

import (
"encoding/binary"
"errors"
"fmt"
"regexp"

"cosmossdk.io/core/store"
upgradetypes "cosmossdk.io/x/upgrade/types"
"github.com/cosmos/cosmos-sdk/codec"
types1 "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1"

Check failure on line 16 in x/compute/internal/keeper/ante.go

View workflow job for this annotation

GitHub Actions / lint

ST1019: package "github.com/cosmos/cosmos-sdk/x/gov/types/v1" is being imported more than once (stylecheck)
v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"

Check failure on line 17 in x/compute/internal/keeper/ante.go

View workflow job for this annotation

GitHub Actions / lint

ST1019(related information): other import of "github.com/cosmos/cosmos-sdk/x/gov/types/v1" (stylecheck)
"github.com/scrtlabs/SecretNetwork/x/compute/internal/types"
)

// CountTXDecorator ante handler to count the tx position in a block.
type CountTXDecorator struct {
appcodec codec.Codec
govkeeper govkeeper.Keeper // we need the govkeeper to access stored proposals
storeService store.KVStoreService
}

const msgSoftwareUpgradeTypeURL = "/cosmos.upgrade.v1beta1.MsgSoftwareUpgrade"

// NewCountTXDecorator constructor
func NewCountTXDecorator(storeService store.KVStoreService) *CountTXDecorator {
return &CountTXDecorator{storeService: storeService}
func NewCountTXDecorator(appcodec codec.Codec, govkeeper govkeeper.Keeper, storeService store.KVStoreService) *CountTXDecorator {
return &CountTXDecorator{
appcodec: appcodec,
govkeeper: govkeeper,
storeService: storeService,
}
}

// Function to find and return the MREnclaveHash string from input
func findMREnclaveHash(input string) (string, error) {
// Define the regular expression pattern with a capture group for the SHA256 hash
pattern := `^MREnclaveHash:([a-fA-F0-9]{64})$`

re := regexp.MustCompile(pattern)

matches := re.FindStringSubmatch(input)

// If no match is found, return an error
if len(matches) < 2 {
return "", errors.New("MREnclaveHash not found or invalid in the input string")
}

// The SHA256 hash is captured in the first capturing group, which is matches[1]
return matches[1], nil
}

// AnteHandle handler stores a tx counter with current height encoded in the store to let the app handle
Expand Down Expand Up @@ -44,9 +80,76 @@ func (a CountTXDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool,
ctx.Logger().Error("compute ante store set", "store", err.Error())
}

for _, msg := range tx.GetMsgs() {
// Check if this is a MsgUpgradeProposalPassed
msgUpgrade, ok := msg.(*types.MsgUpgradeProposalPassed)
if ok {
err = a.verifyUpgradeProposal(ctx, msgUpgrade)
if err != nil {
return ctx, err
}
}
}

return next(types.WithTXCounter(ctx, txCounter), tx, simulate)
}

// extractInfoFromProposalMessages extracts the "info" field from the proposal message.
// This "info" contains the MREnclaveHash.
func extractInfoFromProposalMessages(message *types1.Any, cdc codec.Codec) (string, error) {
var softwareUpgradeMsg *upgradetypes.MsgSoftwareUpgrade
err := cdc.UnpackAny(message, &softwareUpgradeMsg)
if err != nil {
return "", fmt.Errorf("failed to unpack message: %w", err)
}

return softwareUpgradeMsg.Plan.Info, nil
}

// verifyUpgradeProposal verifies the latest passed upgrade proposal to ensure the MREnclave hash matches.
func (a *CountTXDecorator) verifyUpgradeProposal(ctx sdk.Context, msgUpgrade *types.MsgUpgradeProposalPassed) error {
var proposals govtypes.Proposals
err := a.govkeeper.Proposals.Walk(ctx, nil, func(_ uint64, value govtypes.Proposal) (stop bool, err error) {
proposals = append(proposals, &value)
return false, nil
})
if err != nil {
ctx.Logger().Error("gov keeper", "proposal", err.Error())
return err
}

var latestProposal *v1.Proposal = nil

Check warning on line 121 in x/compute/internal/keeper/ante.go

View workflow job for this annotation

GitHub Actions / lint

var-declaration: should drop = nil from declaration of var latestProposal; it is the zero value (revive)
var latestMREnclaveHash string

// Iterate through the proposals
for _, proposal := range proposals {
// Check if the proposal has passed and is of type MsgSoftwareUpgrade
if proposal.Status == v1.ProposalStatus_PROPOSAL_STATUS_PASSED {
if len(proposal.GetMessages()) > 0 && proposal.Messages[0].GetTypeUrl() == msgSoftwareUpgradeTypeURL {
// Update latestProposal if this proposal is newer (has a higher ID)
if latestProposal == nil || proposal.Id > latestProposal.Id {
latestProposal = proposal
}
}
}
}

// If we found the MsgSoftwareUpgrade latest passed proposal, extract the MREnclaveHash from it
if latestProposal != nil {
info, err := extractInfoFromProposalMessages(latestProposal.Messages[0], a.appcodec)
if err != nil {
return fmt.Errorf("Failed to extract info with MREnclave hash from Proposal, error: %w", err)

Check failure on line 141 in x/compute/internal/keeper/ante.go

View workflow job for this annotation

GitHub Actions / lint

ST1005: error strings should not be capitalized (stylecheck)
}
latestMREnclaveHash, _ = findMREnclaveHash(info)
}

// Check if the MREnclave hash matches the one in the MsgUpgradeProposalPassed message
if latestMREnclaveHash != string(msgUpgrade.MrEnclaveHash) {
return sdkerrors.ErrUnauthorized.Wrap("software upgrade proposal: mrenclave hash mismatch")
}
return nil
}

func encodeHeightCounter(height int64, counter uint32) []byte {
b := make([]byte, 4)
binary.BigEndian.PutUint32(b, counter)
Expand Down
16 changes: 16 additions & 0 deletions x/compute/internal/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,3 +192,19 @@ func (m msgServer) ClearAdmin(goCtx context.Context, msg *types.MsgClearAdmin) (

return &types.MsgClearAdminResponse{}, nil
}

func (m msgServer) UpgradeProposalPassed(goCtx context.Context, msg *types.MsgUpgradeProposalPassed) (*types.MsgUpgradeProposalPassedResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)

if err := msg.ValidateBasic(); err != nil {
return nil, err
}

ctx.EventManager().EmitEvent(sdk.NewEvent(
types.EventTypeExecute,
sdk.NewAttribute(sdk.AttributeKeySender, msg.SenderAddress),
sdk.NewAttribute("mrenclave", string(msg.MrEnclaveHash)),
))

return &types.MsgUpgradeProposalPassedResponse{}, nil
}
19 changes: 10 additions & 9 deletions x/compute/internal/types/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@ const (
// CustomContractEventPrefix contracts can create custom events. To not mix them with other system events they got the `wasm-` prefix.
CustomContractEventPrefix = "wasm-"

EventTypeStoreCode = "store_code"
EventTypeInstantiate = "instantiate"
EventTypeExecute = "execute"
EventTypeMigrate = "migrate"
EventTypePinCode = "pin_code"
EventTypeUnpinCode = "unpin_code"
EventTypeSudo = "sudo"
EventTypeReply = "reply"
EventTypeUpdateContractAdmin = "update_contract_admin"
EventTypeStoreCode = "store_code"
EventTypeInstantiate = "instantiate"
EventTypeExecute = "execute"
EventTypeMigrate = "migrate"
EventTypePinCode = "pin_code"
EventTypeUnpinCode = "unpin_code"
EventTypeSudo = "sudo"
EventTypeReply = "reply"
EventTypeUpdateContractAdmin = "update_contract_admin"
EventTypeUpgradeProposalPassed = "upgrade_proposal_passed"
)

// event attributes returned from contract execution
Expand Down
30 changes: 30 additions & 0 deletions x/compute/internal/types/msg.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,3 +215,33 @@ func (msg MsgClearAdmin) GetSigners() []sdk.AccAddress {
}
return []sdk.AccAddress{senderAddr}
}

func (msg MsgUpgradeProposalPassed) Route() string {
return RouterKey
}

func (msg MsgUpgradeProposalPassed) Type() string {
return "upgrade-proposal-passed"
}

func (msg MsgUpgradeProposalPassed) ValidateBasic() error {
if err := sdk.VerifyAddressFormat([]byte(msg.SenderAddress)); err != nil {
return err
}
if len(msg.MrEnclaveHash) != 64 {
return sdkerrors.ErrInvalidRequest.Wrap("MREnclave hash length is not equal 64!")
}
return nil
}

func (msg MsgUpgradeProposalPassed) GetSignBytes() []byte {
return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&msg))
}

func (msg MsgUpgradeProposalPassed) GetSigners() []sdk.AccAddress {
senderAddr, err := sdk.AccAddressFromBech32(msg.SenderAddress)
if err != nil { // should never happen as valid basic rejects invalid addresses
panic(err.Error())
}
return []sdk.AccAddress{senderAddr}
}
Loading

0 comments on commit 3cf353d

Please sign in to comment.