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 }