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