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 }