github.com/Finschia/finschia-sdk@v0.49.1/x/foundation/keeper/internal/exec.go (about)

     1  package internal
     2  
     3  import (
     4  	"fmt"
     5  
     6  	sdk "github.com/Finschia/finschia-sdk/types"
     7  	sdkerrors "github.com/Finschia/finschia-sdk/types/errors"
     8  	"github.com/Finschia/finschia-sdk/x/foundation"
     9  )
    10  
    11  // ensureMsgAuthz checks that if a message requires signers that all of them are equal to the given account address of the authority.
    12  func ensureMsgAuthz(msgs []sdk.Msg, authority sdk.AccAddress) error {
    13  	for _, msg := range msgs {
    14  		// In practice, GetSigners() should return a non-empty array without
    15  		// duplicates, so the code below is equivalent to:
    16  		// `msgs[i].GetSigners()[0] == authority`
    17  		// but we prefer to loop through all GetSigners just to be sure.
    18  		for _, signer := range msg.GetSigners() {
    19  			if !authority.Equals(signer) {
    20  				return sdkerrors.ErrUnauthorized.Wrapf("bad signer; expected %s, got %s", authority, signer)
    21  			}
    22  		}
    23  	}
    24  	return nil
    25  }
    26  
    27  func (k Keeper) Exec(ctx sdk.Context, proposalID uint64) error {
    28  	proposal, err := k.GetProposal(ctx, proposalID)
    29  	if err != nil {
    30  		return err
    31  	}
    32  
    33  	if proposal.Status != foundation.PROPOSAL_STATUS_SUBMITTED &&
    34  		proposal.Status != foundation.PROPOSAL_STATUS_ACCEPTED {
    35  		return sdkerrors.ErrInvalidRequest.Wrapf("not possible with proposal status: %s", proposal.Status)
    36  	}
    37  
    38  	if proposal.Status == foundation.PROPOSAL_STATUS_SUBMITTED {
    39  		if err := k.doTallyAndUpdate(ctx, proposal); err != nil {
    40  			return err
    41  		}
    42  	}
    43  
    44  	// Execute proposal payload.
    45  	var logs string
    46  	if proposal.Status == foundation.PROPOSAL_STATUS_ACCEPTED &&
    47  		proposal.ExecutorResult != foundation.PROPOSAL_EXECUTOR_RESULT_SUCCESS {
    48  		logger := ctx.Logger().With("module", fmt.Sprintf("x/%s", foundation.ModuleName))
    49  		// Caching context so that we don't update the store in case of failure.
    50  		cacheCtx, flush := ctx.CacheContext()
    51  
    52  		if results, err := k.doExecuteMsgs(cacheCtx, *proposal); err != nil {
    53  			proposal.ExecutorResult = foundation.PROPOSAL_EXECUTOR_RESULT_FAILURE
    54  			logs = fmt.Sprintf("proposal execution failed on proposal %d, because of error %s", proposalID, err.Error())
    55  			logger.Info("proposal execution failed", "cause", err, "proposalID", proposal.Id)
    56  		} else {
    57  			proposal.ExecutorResult = foundation.PROPOSAL_EXECUTOR_RESULT_SUCCESS
    58  			flush()
    59  
    60  			for _, res := range results {
    61  				// NOTE: The sdk msg handler creates a new EventManager, so events must be correctly propagated back to the current context
    62  				ctx.EventManager().EmitEvents(res.GetEvents())
    63  			}
    64  		}
    65  	}
    66  
    67  	// If proposal has successfully run, delete it from state.
    68  	if proposal.ExecutorResult == foundation.PROPOSAL_EXECUTOR_RESULT_SUCCESS {
    69  		k.pruneProposal(ctx, *proposal)
    70  	} else {
    71  		k.setProposal(ctx, *proposal)
    72  	}
    73  
    74  	if err := ctx.EventManager().EmitTypedEvent(&foundation.EventExec{
    75  		ProposalId: proposal.Id,
    76  		Logs:       logs,
    77  		Result:     proposal.ExecutorResult,
    78  	}); err != nil {
    79  		panic(err)
    80  	}
    81  
    82  	return nil
    83  }
    84  
    85  // doExecuteMsgs routes the messages to the registered handlers.
    86  func (k Keeper) doExecuteMsgs(ctx sdk.Context, proposal foundation.Proposal) ([]sdk.Result, error) {
    87  	msgs := proposal.GetMsgs()
    88  	results := make([]sdk.Result, len(msgs))
    89  
    90  	authority := sdk.MustAccAddressFromBech32(k.GetAuthority())
    91  	if err := ensureMsgAuthz(msgs, authority); err != nil {
    92  		return nil, err
    93  	}
    94  
    95  	for i, msg := range msgs {
    96  		handler := k.router.Handler(msg)
    97  		if handler == nil {
    98  			return nil, sdkerrors.ErrUnknownRequest.Wrapf("no message handler found for %q", sdk.MsgTypeURL(msg))
    99  		}
   100  		r, err := handler(ctx, msg)
   101  		if err != nil {
   102  			return nil, sdkerrors.Wrapf(err, "message %q at position %d", msg, i)
   103  		}
   104  		if r != nil {
   105  			results[i] = *r
   106  		}
   107  	}
   108  	return results, nil
   109  }