@@ -2,11 +2,14 @@ package vm
2
2
3
3
import (
4
4
"context"
5
+ "encoding/hex"
5
6
"fmt"
6
7
7
8
"github.com/filecoin-project/go-address"
9
+ "github.com/filecoin-project/go-state-types/exitcode"
8
10
"github.com/filecoin-project/lotus/chain/types"
9
11
"github.com/ipfs/go-cid"
12
+ logging "github.com/ipfs/go-log/v2"
10
13
"go.opentelemetry.io/otel"
11
14
"go.opentelemetry.io/otel/attribute"
12
15
"golang.org/x/sync/errgroup"
@@ -20,6 +23,8 @@ import (
20
23
messages "github.com/filecoin-project/lily/tasks/messages"
21
24
)
22
25
26
+ var log = logging .Logger ("lily/tasks/vmmsg" )
27
+
23
28
type Task struct {
24
29
node tasks.DataSource
25
30
}
@@ -85,26 +90,73 @@ func (t *Task) ProcessTipSets(ctx context.Context, current *types.TipSet, execut
85
90
default :
86
91
}
87
92
93
+ // TODO this loop could be parallelized if it becomes a bottleneck.
94
+ // NB: the getActorCode method is the expensive call since it resolves addresses and may load the statetree.
88
95
for _ , child := range util .GetChildMessagesOf (parentMsg ) {
89
96
// Cid() computes a CID, so only call it once
90
97
childCid := child .Message .Cid ()
91
- toCode , ok := getActorCode (child .Message .To )
92
- if ! ok {
98
+
99
+ toCode , found := getActorCode (child .Message .To )
100
+ if ! found && child .Receipt .ExitCode == 0 {
101
+ // No destination actor code. Normally Lotus will create an account actor for unknown addresses but if the
102
+ // message fails then Lotus will not allow the actor to be created, and we are left with an address of an
103
+ // unknown type.
104
+ // If the message was executed it means we are out of step with Lotus behaviour somehow. This probably
105
+ // indicates that Lily actor type detection is out of date.
106
+ log .Errorw ("parsing VM message" , "source_cid" , parentMsg .Cid , "source_receipt" , parentMsg .Ret , "child_cid" , childCid , "child_receipt" , child .Receipt )
93
107
errorsDetected = append (errorsDetected , & messages.MessageError {
94
108
Cid : parentMsg .Cid ,
95
- Error : fmt .Errorf ("failed to get to actor code for message: %s" , childCid ).Error (),
109
+ Error : fmt .Errorf ("failed to get to actor code for message: %s to address %s" , childCid , child .Message .To ).Error (),
110
+ })
111
+ continue
112
+ }
113
+
114
+ // if the to actor code was not found we cannot parse params or return, record the message and continue
115
+ if ! found ||
116
+ // if the exit code indicates an issue with params or method we cannot parse the message params
117
+ child .Receipt .ExitCode == exitcode .ErrSerialization ||
118
+ child .Receipt .ExitCode == exitcode .ErrIllegalArgument ||
119
+ child .Receipt .ExitCode == exitcode .SysErrInvalidMethod ||
120
+ // UsrErrUnsupportedMethod TODO: https://github.com/filecoin-project/go-state-types/pull/44
121
+ child .Receipt .ExitCode == exitcode .ExitCode (22 ) {
122
+
123
+ // append results and continue
124
+ vmMessageResults = append (vmMessageResults , & messagemodel.VMMessage {
125
+ Height : int64 (parentMsg .Height ),
126
+ StateRoot : parentMsg .StateRoot .String (),
127
+ Source : parentMsg .Cid .String (),
128
+ Cid : childCid .String (),
129
+ From : child .Message .From .String (),
130
+ To : child .Message .To .String (),
131
+ Value : child .Message .Value .String (),
132
+ GasUsed : child .Receipt .GasUsed ,
133
+ ExitCode : int64 (child .Receipt .ExitCode ), // exit code is guaranteed to be non-zero which will indicate why actor was not found (i.e. message that created the actor failed to apply)
134
+ ActorCode : toCode .String (), // since the actor code wasn't found this will be the string of an undefined CID.
135
+ Method : uint64 (child .Message .Method ),
136
+ Params : "" ,
137
+ Returns : "" ,
96
138
})
97
139
continue
98
140
}
99
- meta , err := util .MethodParamsReturnForMessage (child , toCode )
141
+
142
+ // the to actor code was found and its exit code indicates the params should be parsable. We can safely
143
+ // attempt to parse message params and return, but exit code may still be non-zero here.
144
+
145
+ params , _ , err := util .ParseParams (child .Message .Params , child .Message .Method , toCode )
100
146
if err != nil {
147
+ // a failure here indicates an error in message param parsing, or in exitcode checks above.
101
148
errorsDetected = append (errorsDetected , & messages.MessageError {
102
- Cid : parentMsg .Cid ,
103
- Error : fmt .Errorf ("failed get child message (%s) metadata: %w" , childCid , err ).Error (),
149
+ Cid : parentMsg .Cid ,
150
+ // hex encode the params for reproduction in a unit test.
151
+ Error : fmt .Errorf ("failed parse child message params cid: %s to code: %s method: %d params (hex encoded): %s : %w" ,
152
+ childCid , toCode , child .Message .Method , hex .EncodeToString (child .Message .Params ), err ).Error (),
104
153
})
154
+ // don't append message to result as it may contain invalud data.
105
155
continue
106
156
}
107
- vmMessageResults = append (vmMessageResults , & messagemodel.VMMessage {
157
+
158
+ // params successfully parsed.
159
+ vmMsg := & messagemodel.VMMessage {
108
160
Height : int64 (parentMsg .Height ),
109
161
StateRoot : parentMsg .StateRoot .String (),
110
162
Source : parentMsg .Cid .String (),
@@ -116,9 +168,30 @@ func (t *Task) ProcessTipSets(ctx context.Context, current *types.TipSet, execut
116
168
ExitCode : int64 (child .Receipt .ExitCode ),
117
169
ActorCode : toCode .String (),
118
170
Method : uint64 (child .Message .Method ),
119
- Params : meta .Params ,
120
- Returns : meta .Return ,
121
- })
171
+ Params : params ,
172
+ // Return will be filled below if exit code is non-zero
173
+ }
174
+
175
+ // only parse return of successful messages since unsuccessful messages don't return a parseable value.
176
+ // As an example: a message may return ErrForbidden, it will have valid params, but will not contain a
177
+ // parsable return value in its receipt.
178
+ if child .Receipt .ExitCode .IsSuccess () {
179
+ ret , _ , err := util .ParseReturn (child .Receipt .Return , child .Message .Method , toCode )
180
+ if err != nil {
181
+ errorsDetected = append (errorsDetected , & messages.MessageError {
182
+ Cid : parentMsg .Cid ,
183
+ // hex encode the return for reproduction in a unit test.
184
+ Error : fmt .Errorf ("failed parse child message return cid: %s to code: %s method: %d return (hex encoded): %s : %w" ,
185
+ childCid , toCode , child .Message .Method , hex .EncodeToString (child .Receipt .Return ), err ).Error (),
186
+ })
187
+ // don't append message to result as it may contain invalid data.
188
+ continue
189
+ }
190
+ // add the message return.
191
+ vmMsg .Returns = ret
192
+ }
193
+ // append message to results
194
+ vmMessageResults = append (vmMessageResults , vmMsg )
122
195
}
123
196
}
124
197
0 commit comments