@@ -25,6 +25,14 @@ import (
25
25
"github.com/filecoin-project/lotus/chain/vm"
26
26
)
27
27
28
+ type execMessageStrategy int
29
+
30
+ const (
31
+ execNoMessages execMessageStrategy = iota // apply no prior or current tipset messages
32
+ execAllMessages // apply all prior and current tipset messages
33
+ execSameSenderMessages // apply all prior messages and any current tipset messages from the same sender
34
+ )
35
+
28
36
var ErrExpensiveFork = errors .New ("refusing explicit call due to state fork at epoch" )
29
37
30
38
// Call applies the given message to the given tipset's parent state, at the epoch following the
@@ -48,12 +56,24 @@ func (sm *StateManager) Call(ctx context.Context, msg *types.Message, ts *types.
48
56
msg .Value = types .NewInt (0 )
49
57
}
50
58
51
- return sm .callInternal (ctx , msg , nil , ts , cid .Undef , sm .GetNetworkVersion , false , false )
59
+ return sm .callInternal (ctx , msg , nil , ts , cid .Undef , sm .GetNetworkVersion , false , execSameSenderMessages )
60
+ }
61
+
62
+ // ApplyOnStateWithGas applies the given message on top of the given state root with gas tracing enabled
63
+ func (sm * StateManager ) ApplyOnStateWithGas (ctx context.Context , stateCid cid.Cid , msg * types.Message , ts * types.TipSet ) (* api.InvocResult , error ) {
64
+ return sm .callInternal (ctx , msg , nil , ts , stateCid , sm .GetNetworkVersion , true , execNoMessages )
52
65
}
53
66
54
67
// CallWithGas calculates the state for a given tipset, and then applies the given message on top of that state.
55
68
func (sm * StateManager ) CallWithGas (ctx context.Context , msg * types.Message , priorMsgs []types.ChainMsg , ts * types.TipSet , applyTsMessages bool ) (* api.InvocResult , error ) {
56
- return sm .callInternal (ctx , msg , priorMsgs , ts , cid .Undef , sm .GetNetworkVersion , true , applyTsMessages )
69
+ var strategy execMessageStrategy
70
+ if applyTsMessages {
71
+ strategy = execAllMessages
72
+ } else {
73
+ strategy = execSameSenderMessages
74
+ }
75
+
76
+ return sm .callInternal (ctx , msg , priorMsgs , ts , cid .Undef , sm .GetNetworkVersion , true , strategy )
57
77
}
58
78
59
79
// CallAtStateAndVersion allows you to specify a message to execute on the given stateCid and network version.
@@ -64,14 +84,14 @@ func (sm *StateManager) CallAtStateAndVersion(ctx context.Context, msg *types.Me
64
84
nvGetter := func (context.Context , abi.ChainEpoch ) network.Version {
65
85
return v
66
86
}
67
-
68
- return sm .callInternal (ctx , msg , nil , nil , stateCid , nvGetter , true , false )
87
+ return sm .callInternal (ctx , msg , nil , nil , stateCid , nvGetter , true , execSameSenderMessages )
69
88
}
70
89
71
90
// - If no tipset is specified, the first tipset without an expensive migration or one in its parent is used.
72
91
// - If executing a message at a given tipset or its parent would trigger an expensive migration, the call will
73
92
// fail with ErrExpensiveFork.
74
- func (sm * StateManager ) callInternal (ctx context.Context , msg * types.Message , priorMsgs []types.ChainMsg , ts * types.TipSet , stateCid cid.Cid , nvGetter rand.NetworkVersionGetter , checkGas , applyTsMessages bool ) (* api.InvocResult , error ) {
93
+ func (sm * StateManager ) callInternal (ctx context.Context , msg * types.Message , priorMsgs []types.ChainMsg , ts * types.TipSet , stateCid cid.Cid ,
94
+ nvGetter rand.NetworkVersionGetter , checkGas bool , strategy execMessageStrategy ) (* api.InvocResult , error ) {
75
95
ctx , span := trace .StartSpan (ctx , "statemanager.callInternal" )
76
96
defer span .End ()
77
97
@@ -95,7 +115,7 @@ func (sm *StateManager) callInternal(ctx context.Context, msg *types.Message, pr
95
115
return nil , xerrors .Errorf ("failed to find a non-forking epoch: %w" , err )
96
116
}
97
117
// Checks for expensive forks from the parents to the tipset, including nil tipsets
98
- if ! sm .hasExpensiveForkBetween (pts .Height (), ts .Height ()+ 1 ) {
118
+ if ! sm .HasExpensiveForkBetween (pts .Height (), ts .Height ()+ 1 ) {
99
119
break
100
120
}
101
121
@@ -106,7 +126,7 @@ func (sm *StateManager) callInternal(ctx context.Context, msg *types.Message, pr
106
126
if err != nil {
107
127
return nil , xerrors .Errorf ("failed to find a non-forking epoch: %w" , err )
108
128
}
109
- if sm .hasExpensiveForkBetween (pts .Height (), ts .Height ()+ 1 ) {
129
+ if sm .HasExpensiveForkBetween (pts .Height (), ts .Height ()+ 1 ) {
110
130
return nil , ErrExpensiveFork
111
131
}
112
132
}
@@ -117,24 +137,6 @@ func (sm *StateManager) callInternal(ctx context.Context, msg *types.Message, pr
117
137
if stateCid == cid .Undef {
118
138
stateCid = ts .ParentState ()
119
139
}
120
- tsMsgs , err := sm .cs .MessagesForTipset (ctx , ts )
121
- if err != nil {
122
- return nil , xerrors .Errorf ("failed to lookup messages for parent tipset: %w" , err )
123
- }
124
-
125
- if applyTsMessages {
126
- priorMsgs = append (tsMsgs , priorMsgs ... )
127
- } else {
128
- var filteredTsMsgs []types.ChainMsg
129
- for _ , tsMsg := range tsMsgs {
130
- //TODO we should technically be normalizing the filecoin address of from when we compare here
131
- if tsMsg .VMMessage ().From == msg .VMMessage ().From {
132
- filteredTsMsgs = append (filteredTsMsgs , tsMsg )
133
- }
134
- }
135
- priorMsgs = append (filteredTsMsgs , priorMsgs ... )
136
- }
137
-
138
140
// Technically, the tipset we're passing in here should be ts+1, but that may not exist.
139
141
stateCid , err = sm .HandleStateForks (ctx , stateCid , ts .Height (), nil , ts )
140
142
if err != nil {
@@ -169,18 +171,40 @@ func (sm *StateManager) callInternal(ctx context.Context, msg *types.Message, pr
169
171
if err != nil {
170
172
return nil , xerrors .Errorf ("failed to set up vm: %w" , err )
171
173
}
172
- for i , m := range priorMsgs {
173
- _ , err = vmi .ApplyMessage (ctx , m )
174
+
175
+ switch strategy {
176
+ case execNoMessages :
177
+ // Do nothing
178
+ case execAllMessages , execSameSenderMessages :
179
+ tsMsgs , err := sm .cs .MessagesForTipset (ctx , ts )
174
180
if err != nil {
175
- return nil , xerrors .Errorf ("applying prior message (%d, %s): %w" , i , m .Cid (), err )
181
+ return nil , xerrors .Errorf ("failed to lookup messages for parent tipset: %w" , err )
182
+ }
183
+ if strategy == execAllMessages {
184
+ priorMsgs = append (tsMsgs , priorMsgs ... )
185
+ } else if strategy == execSameSenderMessages {
186
+ var filteredTsMsgs []types.ChainMsg
187
+ for _ , tsMsg := range tsMsgs {
188
+ //TODO we should technically be normalizing the filecoin address of from when we compare here
189
+ if tsMsg .VMMessage ().From == msg .VMMessage ().From {
190
+ filteredTsMsgs = append (filteredTsMsgs , tsMsg )
191
+ }
192
+ }
193
+ priorMsgs = append (filteredTsMsgs , priorMsgs ... )
194
+ }
195
+ for i , m := range priorMsgs {
196
+ _ , err = vmi .ApplyMessage (ctx , m )
197
+ if err != nil {
198
+ return nil , xerrors .Errorf ("applying prior message (%d, %s): %w" , i , m .Cid (), err )
199
+ }
176
200
}
177
- }
178
201
179
- // We flush to get the VM's view of the state tree after applying the above messages
180
- // This is needed to get the correct nonce from the actor state to match the VM
181
- stateCid , err = vmi .Flush (ctx )
182
- if err != nil {
183
- return nil , xerrors .Errorf ("flushing vm: %w" , err )
202
+ // We flush to get the VM's view of the state tree after applying the above messages
203
+ // This is needed to get the correct nonce from the actor state to match the VM
204
+ stateCid , err = vmi .Flush (ctx )
205
+ if err != nil {
206
+ return nil , xerrors .Errorf ("flushing vm: %w" , err )
207
+ }
184
208
}
185
209
186
210
stTree , err := state .LoadStateTree (cbor .NewCborStore (buffStore ), stateCid )
0 commit comments