@@ -2,11 +2,14 @@ package vm
22
33import (
44 "context"
5+ "encoding/hex"
56 "fmt"
67
78 "github.com/filecoin-project/go-address"
9+ "github.com/filecoin-project/go-state-types/exitcode"
810 "github.com/filecoin-project/lotus/chain/types"
911 "github.com/ipfs/go-cid"
12+ logging "github.com/ipfs/go-log/v2"
1013 "go.opentelemetry.io/otel"
1114 "go.opentelemetry.io/otel/attribute"
1215 "golang.org/x/sync/errgroup"
@@ -20,6 +23,8 @@ import (
2023 messages "github.com/filecoin-project/lily/tasks/messages"
2124)
2225
26+ var log = logging .Logger ("lily/tasks/vmmsg" )
27+
2328type Task struct {
2429 node tasks.DataSource
2530}
@@ -85,26 +90,73 @@ func (t *Task) ProcessTipSets(ctx context.Context, current *types.TipSet, execut
8590 default :
8691 }
8792
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.
8895 for _ , child := range util .GetChildMessagesOf (parentMsg ) {
8996 // Cid() computes a CID, so only call it once
9097 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 )
93107 errorsDetected = append (errorsDetected , & messages.MessageError {
94108 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 : "" ,
96138 })
97139 continue
98140 }
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 )
100146 if err != nil {
147+ // a failure here indicates an error in message param parsing, or in exitcode checks above.
101148 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 (),
104153 })
154+ // don't append message to result as it may contain invalud data.
105155 continue
106156 }
107- vmMessageResults = append (vmMessageResults , & messagemodel.VMMessage {
157+
158+ // params successfully parsed.
159+ vmMsg := & messagemodel.VMMessage {
108160 Height : int64 (parentMsg .Height ),
109161 StateRoot : parentMsg .StateRoot .String (),
110162 Source : parentMsg .Cid .String (),
@@ -116,9 +168,30 @@ func (t *Task) ProcessTipSets(ctx context.Context, current *types.TipSet, execut
116168 ExitCode : int64 (child .Receipt .ExitCode ),
117169 ActorCode : toCode .String (),
118170 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 )
122195 }
123196 }
124197
0 commit comments