@@ -93,121 +93,150 @@ execTxWith executeTx tx = do
93
93
gasLeftBeforeTx <- gets (. state. gas)
94
94
vmResult <- runFully
95
95
gasLeftAfterTx <- gets (. state. gas)
96
- handleErrorsAndConstruction vmResult vmBeforeTx
96
+ handleErrors vmResult vmBeforeTx
97
+ when isCreate $ handleConstruction vmResult tx. dst
97
98
pure (vmResult, gasLeftBeforeTx - gasLeftAfterTx)
98
99
where
99
- runFully = do
100
- config <- asks (. cfg)
101
- -- TODO: Is the latest block a good default? It makes fuzzing hard to reproduce. Rethink this.
102
- let rpcBlock = maybe EVM.Fetch. Latest (EVM.Fetch. BlockNumber . fromIntegral ) config. rpcBlock
100
+ isCreate = case tx. call of
101
+ (SolCreate _) -> True
102
+ _ -> False
103
103
104
+ runFully = do
104
105
vmResult <- executeTx
105
- -- For queries, we halt execution because the VM needs some additional
106
- -- information from the outside. We provide this information and resume
107
- -- the execution by recursively calling `runFully`.
108
- case getQuery vmResult of
109
- -- A previously unknown contract is required
110
- Just q@ (PleaseFetchContract addr _ continuation) -> do
111
- cacheRef <- asks (. fetchContractCache)
112
- cache <- liftIO $ readIORef cacheRef
113
- case Map. lookup addr cache of
114
- Just (Just contract) -> fromEVM (continuation contract)
115
- Just Nothing -> do
116
- v <- get
117
- v' <- liftIO $ stToIO $ execStateT (continuation emptyAccount) v
118
- put v'
119
- Nothing -> do
120
- logMsg $ " INFO: Performing RPC: " <> show q
121
- case config. rpcUrl of
122
- Just rpcUrl -> do
123
- ret <- liftIO $ safeFetchContractFrom rpcBlock rpcUrl addr
124
- case ret of
125
- -- TODO: fix hevm to not return an empty contract in case of an error
126
- Just contract | contract. code /= RuntimeCode (ConcreteRuntimeCode " " ) -> do
127
- fromEVM (continuation contract)
128
- liftIO $ atomicWriteIORef cacheRef $ Map. insert addr (Just contract) cache
129
- _ -> do
130
- -- TODO: better error reporting in HEVM, when intermmittent
131
- -- network error then retry
132
- liftIO $ atomicWriteIORef cacheRef $ Map. insert addr Nothing cache
133
- logMsg $ " ERROR: Failed to fetch contract: " <> show q
134
- -- TODO: How should we fail here? It could be a network error,
135
- -- RPC server returning junk etc.
136
- fromEVM (continuation emptyAccount)
137
- Nothing -> do
138
- liftIO $ atomicWriteIORef cacheRef $ Map. insert addr Nothing cache
139
- logMsg $ " ERROR: Requested RPC but it is not configured: " <> show q
140
- -- TODO: How should we fail here? RPC is not configured but VM
141
- -- wants to fetch
142
- fromEVM (continuation emptyAccount)
143
- runFully -- resume execution
144
-
145
- -- A previously unknown slot is required
146
- Just q@ (PleaseFetchSlot addr slot continuation) -> do
147
- cacheRef <- asks (. fetchSlotCache)
148
- cache <- liftIO $ readIORef cacheRef
149
- case Map. lookup addr cache >>= Map. lookup slot of
150
- Just (Just value) -> fromEVM (continuation value)
151
- Just Nothing -> fromEVM (continuation 0 )
152
- Nothing -> do
153
- logMsg $ " INFO: Performing RPC: " <> show q
154
- case config. rpcUrl of
155
- Just rpcUrl -> do
156
- ret <- liftIO $ safeFetchSlotFrom rpcBlock rpcUrl addr slot
157
- case ret of
158
- Just value -> do
159
- fromEVM (continuation value)
160
- liftIO $ atomicWriteIORef cacheRef $
161
- Map. insertWith Map. union addr (Map. singleton slot (Just value)) cache
162
- Nothing -> do
163
- -- TODO: How should we fail here? It could be a network error,
164
- -- RPC server returning junk etc.
165
- logMsg $ " ERROR: Failed to fetch slot: " <> show q
166
- liftIO $ atomicWriteIORef cacheRef $
167
- Map. insertWith Map. union addr (Map. singleton slot Nothing ) cache
168
- fromEVM (continuation 0 )
169
- Nothing -> do
170
- logMsg $ " ERROR: Requested RPC but it is not configured: " <> show q
171
- -- Use the zero slot
172
- fromEVM (continuation 0 )
173
- runFully -- resume execution
174
-
175
- -- Execute a FFI call
176
- Just (PleaseDoFFI (cmd : args) continuation) -> do
177
- (_, stdout, _) <- liftIO $ readProcessWithExitCode cmd args " "
178
- let encodedResponse = encodeAbiValue $
179
- AbiTuple (V. fromList [AbiBytesDynamic . hexText . T. pack $ stdout])
180
- fromEVM (continuation encodedResponse)
181
- runFully
182
-
183
- -- No queries to answer, the tx is fully executed and the result is final
184
- _ -> pure vmResult
185
-
186
- -- | Handles reverts, failures and contract creations that might be the result
187
- -- (`vmResult`) of executing transaction `tx`.
188
- handleErrorsAndConstruction vmResult vmBeforeTx = case (vmResult, tx. call) of
189
- (Reversion , _) -> do
190
- tracesBeforeVMReset <- gets (. traces)
191
- codeContractBeforeVMReset <- gets (. state. codeContract)
192
- calldataBeforeVMReset <- gets (. state. calldata)
193
- callvalueBeforeVMReset <- gets (. state. callvalue)
194
- -- If a transaction reverts reset VM to state before the transaction.
195
- put vmBeforeTx
196
- -- Undo reset of some of the VM state.
197
- -- Otherwise we'd loose all information about the reverted transaction like
198
- -- contract address, calldata, result and traces.
199
- # result ?= vmResult
200
- # state % # calldata .= calldataBeforeVMReset
201
- # state % # callvalue .= callvalueBeforeVMReset
202
- # traces .= tracesBeforeVMReset
203
- # state % # codeContract .= codeContractBeforeVMReset
204
- (VMFailure x, _) -> vmExcept x
205
- (VMSuccess (ConcreteBuf bytecode'), SolCreate _) -> do
206
- -- Handle contract creation.
207
- # env % # contracts % at (LitAddr tx. dst) % _Just % # code .= InitCode mempty mempty
208
- fromEVM $ replaceCodeOfSelf (RuntimeCode (ConcreteRuntimeCode bytecode'))
209
- modify' $ execState $ loadContract (LitAddr tx. dst)
210
- _ -> pure ()
106
+ maybe (pure vmResult) (\ q -> handleQuery q >> runFully) (getQuery vmResult)
107
+
108
+ getRpcInfo
109
+ :: (MonadReader Env m )
110
+ => m (Maybe T. Text , EVM.Fetch. BlockNumber )
111
+ getRpcInfo = do
112
+ config <- asks (. cfg)
113
+ -- TODO: Is the latest block a good default? It makes fuzzing hard to reproduce. Rethink this.
114
+ let rpcBlock = maybe EVM.Fetch. Latest (EVM.Fetch. BlockNumber . fromIntegral ) config. rpcBlock
115
+ return (config. rpcUrl, rpcBlock)
116
+
117
+ handleQuery
118
+ :: (MonadIO m , MonadState (VM Concrete RealWorld ) m , MonadReader Env m , MonadThrow m )
119
+ => Query Concrete RealWorld
120
+ -> m ()
121
+
122
+ -- For queries, we halt execution because the VM needs some additional
123
+ -- information from the outside. We provide this information and resume
124
+ -- the execution by recursively calling `runFully`.
125
+ handleQuery q@ (PleaseFetchContract addr _ continuation) = do
126
+ -- A previously unknown contract is required
127
+ cacheRef <- asks (. fetchContractCache)
128
+ cache <- liftIO $ readIORef cacheRef
129
+ case Map. lookup addr cache of
130
+ Just (Just contract) -> fromEVM (continuation contract)
131
+ Just Nothing -> do
132
+ v <- get
133
+ v' <- liftIO $ stToIO $ execStateT (continuation emptyAccount) v
134
+ put v'
135
+ Nothing -> do
136
+ logMsg $ " INFO: Performing RPC: " <> show q
137
+ (maybeRpcUrl, rpcBlock) <- getRpcInfo
138
+ case maybeRpcUrl of
139
+ Just rpcUrl -> do
140
+ ret <- liftIO $ safeFetchContractFrom rpcBlock rpcUrl addr
141
+ case ret of
142
+ -- TODO: fix hevm to not return an empty contract in case of an error
143
+ Just contract | contract. code /= RuntimeCode (ConcreteRuntimeCode " " ) -> do
144
+ fromEVM (continuation contract)
145
+ liftIO $ atomicWriteIORef cacheRef $ Map. insert addr (Just contract) cache
146
+ _ -> do
147
+ -- TODO: better error reporting in HEVM, when intermmittent
148
+ -- network error then retry
149
+ liftIO $ atomicWriteIORef cacheRef $ Map. insert addr Nothing cache
150
+ logMsg $ " ERROR: Failed to fetch contract: " <> show q
151
+ -- TODO: How should we fail here? It could be a network error,
152
+ -- RPC server returning junk etc.
153
+ fromEVM (continuation emptyAccount)
154
+ Nothing -> do
155
+ liftIO $ atomicWriteIORef cacheRef $ Map. insert addr Nothing cache
156
+ logMsg $ " ERROR: Requested RPC but it is not configured: " <> show q
157
+ -- TODO: How should we fail here? RPC is not configured but VM
158
+ -- wants to fetch
159
+ fromEVM (continuation emptyAccount)
160
+
161
+ -- A previously unknown slot is required
162
+ handleQuery q@ (PleaseFetchSlot addr slot continuation) = do
163
+ cacheRef <- asks (. fetchSlotCache)
164
+ cache <- liftIO $ readIORef cacheRef
165
+ case Map. lookup addr cache >>= Map. lookup slot of
166
+ Just (Just value) -> fromEVM (continuation value)
167
+ Just Nothing -> fromEVM (continuation 0 )
168
+ Nothing -> do
169
+ logMsg $ " INFO: Performing RPC: " <> show q
170
+ (maybeRpcUrl, rpcBlock) <- getRpcInfo
171
+ case maybeRpcUrl of
172
+ Just rpcUrl -> do
173
+ ret <- liftIO $ safeFetchSlotFrom rpcBlock rpcUrl addr slot
174
+ case ret of
175
+ Just value -> do
176
+ fromEVM (continuation value)
177
+ liftIO $ atomicWriteIORef cacheRef $
178
+ Map. insertWith Map. union addr (Map. singleton slot (Just value)) cache
179
+ Nothing -> do
180
+ -- TODO: How should we fail here? It could be a network error,
181
+ -- RPC server returning junk etc.
182
+ logMsg $ " ERROR: Failed to fetch slot: " <> show q
183
+ liftIO $ atomicWriteIORef cacheRef $
184
+ Map. insertWith Map. union addr (Map. singleton slot Nothing ) cache
185
+ fromEVM (continuation 0 )
186
+ Nothing -> do
187
+ logMsg $ " ERROR: Requested RPC but it is not configured: " <> show q
188
+ -- Use the zero slot
189
+ fromEVM (continuation 0 )
190
+
191
+ -- Execute a FFI call
192
+ handleQuery (PleaseDoFFI (cmd : args) continuation) = do
193
+ (_, stdout, _) <- liftIO $ readProcessWithExitCode cmd args " "
194
+ let encodedResponse = encodeAbiValue $
195
+ AbiTuple (V. fromList [AbiBytesDynamic . hexText . T. pack $ stdout])
196
+ fromEVM (continuation encodedResponse)
197
+
198
+ handleQuery (PleaseDoFFI [] _) = error " Malformed FFI call"
199
+
200
+ handleErrors
201
+ :: (MonadIO m , MonadState (VM Concrete RealWorld ) m , MonadReader Env m , MonadThrow m )
202
+ => VMResult Concrete RealWorld
203
+ -> VM Concrete RealWorld
204
+ -> m ()
205
+
206
+ -- | Handles reverts, failures and contract creations that might be the result
207
+ -- (`vmResult`) of executing transaction `tx`.
208
+ handleErrors vmResult@ Reversion vmBeforeTx = do
209
+ tracesBeforeVMReset <- gets (. traces)
210
+ codeContractBeforeVMReset <- gets (. state. codeContract)
211
+ calldataBeforeVMReset <- gets (. state. calldata)
212
+ callvalueBeforeVMReset <- gets (. state. callvalue)
213
+ -- If a transaction reverts reset VM to state before the transaction.
214
+ put vmBeforeTx
215
+ -- Undo reset of some of the VM state.
216
+ -- Otherwise we'd loose all information about the reverted transaction like
217
+ -- contract address, calldata, result and traces.
218
+ # result ?= vmResult
219
+ # state % # calldata .= calldataBeforeVMReset
220
+ # state % # callvalue .= callvalueBeforeVMReset
221
+ # traces .= tracesBeforeVMReset
222
+ # state % # codeContract .= codeContractBeforeVMReset
223
+ handleErrors (VMFailure x) _ = vmExcept x
224
+ handleErrors _ _ = pure ()
225
+
226
+ handleConstruction
227
+ :: (MonadIO m , MonadState (VM Concrete RealWorld ) m , MonadReader Env m , MonadThrow m )
228
+ => VMResult Concrete s
229
+ -> Addr
230
+ -> m ()
231
+
232
+ -- | Handles reverts, failures and contract creations that might be the result
233
+ -- (`vmResult`) of executing transaction `tx`.
234
+ handleConstruction (VMSuccess (ConcreteBuf bytecode')) dst = do
235
+ -- Handle contract creation.
236
+ # env % # contracts % at (LitAddr dst) % _Just % # code .= InitCode mempty mempty
237
+ fromEVM $ replaceCodeOfSelf (RuntimeCode (ConcreteRuntimeCode bytecode'))
238
+ modify' $ execState $ loadContract (LitAddr dst)
239
+ handleConstruction _ _ = pure ()
211
240
212
241
logMsg :: (MonadIO m , MonadReader Env m ) => String -> m ()
213
242
logMsg msg = do
0 commit comments