forked from cosmos/cosmos-sdk
-
Notifications
You must be signed in to change notification settings - Fork 0
/
run_msgs.go
165 lines (141 loc) · 5.74 KB
/
run_msgs.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
package middleware
import (
"context"
"fmt"
"strings"
"github.com/gogo/protobuf/proto"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto/tmhash"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/types/tx"
"github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx"
)
type runMsgsTxHandler struct {
legacyRouter sdk.Router // router for redirecting legacy Msgs
msgServiceRouter *MsgServiceRouter // router for redirecting Msg service messages
}
func NewRunMsgsTxHandler(msr *MsgServiceRouter, legacyRouter sdk.Router) tx.Handler {
return runMsgsTxHandler{
legacyRouter: legacyRouter,
msgServiceRouter: msr,
}
}
var _ tx.Handler = runMsgsTxHandler{}
// CheckTx implements tx.Handler.CheckTx method.
func (txh runMsgsTxHandler) CheckTx(ctx context.Context, tx sdk.Tx, req abci.RequestCheckTx) (abci.ResponseCheckTx, error) {
// Don't run Msgs during CheckTx.
return abci.ResponseCheckTx{}, nil
}
// DeliverTx implements tx.Handler.DeliverTx method.
func (txh runMsgsTxHandler) DeliverTx(ctx context.Context, tx sdk.Tx, req abci.RequestDeliverTx) (abci.ResponseDeliverTx, error) {
res, err := txh.runMsgs(sdk.UnwrapSDKContext(ctx), tx.GetMsgs(), req.Tx)
if err != nil {
return abci.ResponseDeliverTx{}, err
}
return abci.ResponseDeliverTx{
// GasInfo will be populated by the Gas middleware.
Log: res.Log,
Data: res.Data,
Events: res.Events,
}, nil
}
// SimulateTx implements tx.Handler.SimulateTx method.
func (txh runMsgsTxHandler) SimulateTx(ctx context.Context, sdkTx sdk.Tx, req tx.RequestSimulateTx) (tx.ResponseSimulateTx, error) {
res, err := txh.runMsgs(sdk.UnwrapSDKContext(ctx), sdkTx.GetMsgs(), req.TxBytes)
if err != nil {
return tx.ResponseSimulateTx{}, err
}
return tx.ResponseSimulateTx{
// GasInfo will be populated by the Gas middleware.
Result: res,
}, nil
}
// runMsgs iterates through a list of messages and executes them with the provided
// Context and execution mode. Messages will only be executed during simulation
// and DeliverTx. An error is returned if any single message fails or if a
// Handler does not exist for a given message route. Otherwise, a reference to a
// Result is returned. The caller must not commit state if an error is returned.
func (txh runMsgsTxHandler) runMsgs(sdkCtx sdk.Context, msgs []sdk.Msg, txBytes []byte) (*sdk.Result, error) {
// Create a new Context based off of the existing Context with a MultiStore branch
// in case message processing fails. At this point, the MultiStore
// is a branch of a branch.
runMsgCtx, msCache := cacheTxContext(sdkCtx, txBytes)
// Attempt to execute all messages and only update state if all messages pass
// and we're in DeliverTx. Note, runMsgs will never return a reference to a
// Result if any single message fails or does not have a registered Handler.
msgLogs := make(sdk.ABCIMessageLogs, 0, len(msgs))
events := sdkCtx.EventManager().Events()
txMsgData := &sdk.TxMsgData{
Data: make([]*sdk.MsgData, 0, len(msgs)),
}
// NOTE: GasWanted is determined by the Gas TxHandler and GasUsed by the GasMeter.
for i, msg := range msgs {
var (
msgResult *sdk.Result
eventMsgName string // name to use as value in event `message.action`
err error
)
if handler := txh.msgServiceRouter.Handler(msg); handler != nil {
// ADR 031 request type routing
msgResult, err = handler(runMsgCtx, msg)
eventMsgName = sdk.MsgTypeURL(msg)
} else if legacyMsg, ok := msg.(legacytx.LegacyMsg); ok {
// legacy sdk.Msg routing
// Assuming that the app developer has migrated all their Msgs to
// proto messages and has registered all `Msg services`, then this
// path should never be called, because all those Msgs should be
// registered within the `MsgServiceRouter` already.
msgRoute := legacyMsg.Route()
eventMsgName = legacyMsg.Type()
handler := txh.legacyRouter.Route(sdkCtx, msgRoute)
if handler == nil {
return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized message route: %s; message index: %d", msgRoute, i)
}
msgResult, err = handler(sdkCtx, msg)
} else {
return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "can't route message %+v", msg)
}
if err != nil {
return nil, sdkerrors.Wrapf(err, "failed to execute message; message index: %d", i)
}
msgEvents := sdk.Events{
sdk.NewEvent(sdk.EventTypeMessage, sdk.NewAttribute(sdk.AttributeKeyAction, eventMsgName)),
}
msgEvents = msgEvents.AppendEvents(msgResult.GetEvents())
// append message events, data and logs
//
// Note: Each message result's data must be length-prefixed in order to
// separate each result.
events = events.AppendEvents(msgEvents)
txMsgData.Data = append(txMsgData.Data, &sdk.MsgData{MsgType: sdk.MsgTypeURL(msg), Data: msgResult.Data})
msgLogs = append(msgLogs, sdk.NewABCIMessageLog(uint32(i), msgResult.Log, msgEvents))
}
msCache.Write()
data, err := proto.Marshal(txMsgData)
if err != nil {
return nil, sdkerrors.Wrap(err, "failed to marshal tx data")
}
return &sdk.Result{
Data: data,
Log: strings.TrimSpace(msgLogs.String()),
Events: events.ToABCIEvents(),
}, nil
}
// cacheTxContext returns a new context based off of the provided context with
// a branched multi-store.
func cacheTxContext(sdkCtx sdk.Context, txBytes []byte) (sdk.Context, sdk.CacheMultiStore) {
ms := sdkCtx.MultiStore()
// TODO: https://github.com/cosmos/cosmos-sdk/issues/2824
msCache := ms.CacheMultiStore()
if msCache.TracingEnabled() {
msCache = msCache.SetTracingContext(
sdk.TraceContext(
map[string]interface{}{
"txHash": fmt.Sprintf("%X", tmhash.Sum(txBytes)),
},
),
).(sdk.CacheMultiStore)
}
return sdkCtx.WithMultiStore(msCache), msCache
}