Skip to content

Commit

Permalink
added antehandler logic for mrenclave upgrade
Browse files Browse the repository at this point in the history
  • Loading branch information
cboh4 committed Sep 10, 2024
1 parent 28f85a6 commit 21ee616
Show file tree
Hide file tree
Showing 9 changed files with 623 additions and 79 deletions.
4 changes: 3 additions & 1 deletion app/ante.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"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 +16,7 @@ import (
type HandlerOptions struct {
ante.HandlerOptions

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 +41,7 @@ func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) {
}

anteDecorators := []sdk.AnteDecorator{
compute.NewCountTXDecorator(options.TXCounterStoreService),
compute.NewCountTXDecorator(options.TXCounterStoreService, options.govkeeper),
ante.NewSetUpContextDecorator(), // outermost AnteDecorator. SetUpContext must be called first
ante.NewExtensionOptionsDecorator(nil),
ante.NewValidateBasicDecorator(),
Expand Down
6 changes: 6 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 @@ -174,6 +175,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 @@ -369,6 +374,7 @@ func NewSecretNetworkApp(
SignModeHandler: app.txConfig.SignModeHandler(),
SigGasConsumer: ante.DefaultSigVerificationGasConsumer,
},
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
}
72 changes: 70 additions & 2 deletions x/compute/internal/keeper/ante.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,48 @@ package keeper

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

"cosmossdk.io/core/store"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
"github.com/scrtlabs/SecretNetwork/x/compute/internal/types"

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

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

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

// 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,6 +72,46 @@ 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 {
iterator, err := a.govkeeper.Proposals.Iterate(ctx, nil)
if err != nil {
ctx.Logger().Error("Failed to get the iterator of proposals!", err.Error())
}

defer iterator.Close() // Ensure the iterator is closed after use

var latestProposal *v1.Proposal
var latestMREnclaveHash string

// Iterate through the proposals
for ; iterator.Valid(); iterator.Next() {
// Get the proposal value
proposal, err := iterator.Value()
if err != nil {
ctx.Logger().Error("Failed to get the proposal from iterator!", err.Error())
}

mrenclaveHash, err := findMREnclaveHash(proposal.Metadata)
// Apply filter: Check if the proposal has "passed"
if err == nil && proposal.Status == v1.ProposalStatus_PROPOSAL_STATUS_PASSED {
// Check if this is the latest passed proposal by id
if latestProposal == nil || proposal.Id > latestProposal.Id {
latestProposal = &proposal
latestMREnclaveHash = mrenclaveHash
}
}
}

// Retrieve the stored mrenclave hash from the keeper
if latestMREnclaveHash != string(msgUpgrade.MrEnclaveHash) {
return ctx, sdkerrors.ErrUnauthorized.Wrap("mrenclave hash mismatch")
}
}
}

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

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 21ee616

Please sign in to comment.