1use super::utils::structured;
5use super::*;
6use crate::interpreter::{ExecutionContext, IMPLICIT_MESSAGE_GAS_LIMIT, VM, VMTrace};
7use crate::message::{MessageRead as _, MessageReadWrite as _};
8use crate::rpc::state::{ApiInvocResult, MessageGasCost};
9use crate::shim::executor::ApplyRet;
10use crate::shim::message::Message;
11use crate::state_migration::run_state_migrations;
12use std::time::Duration;
13use tracing::instrument;
14
15impl StateManager {
16 #[instrument(skip(self))]
17 fn call_raw_blocking(
18 &self,
19 state_cid: Option<Cid>,
20 msg: &Message,
21 tipset: Option<Tipset>,
22 ) -> Result<ApiInvocResult, Error> {
23 let mut msg = msg.clone();
24 let chain_config = self.chain_config();
25
26 let tipset = if let Some(ts) = tipset {
27 if ts.epoch() > 0 {
28 let (fork_floor, fork_height) = if state_cid.is_some() {
32 (ts.epoch(), ts.epoch() + 1)
33 } else {
34 let parent = self
35 .chain_index()
36 .load_required_tipset(ts.parents())
37 .map_err(Error::other)?;
38 (parent.epoch(), ts.epoch() + 1)
39 };
40 if let Some(epoch) = chain_config.expensive_fork_between(fork_floor, fork_height) {
41 return Err(Error::ExpensiveFork { epoch });
42 }
43 }
44 ts
45 } else {
46 let mut heaviest_ts = self.heaviest_tipset();
48 while heaviest_ts.epoch() > 0 {
49 let parent = self
50 .chain_index()
51 .load_required_tipset(heaviest_ts.parents())
52 .map_err(Error::other)?;
53 if !chain_config.has_expensive_fork_between(parent.epoch(), heaviest_ts.epoch() + 1)
54 {
55 break;
56 }
57 heaviest_ts = parent;
58 }
59 heaviest_ts
60 };
61
62 let state_cid = state_cid.unwrap_or(*tipset.parent_state());
63
64 let state_cid = match run_state_migrations(
66 tipset.epoch(),
67 self.chain_config(),
68 self.db(),
69 &state_cid,
70 ) {
71 Ok(Some(new_state)) => new_state,
72 Ok(None) => state_cid,
73 Err(e) => return Err(Error::other(e)),
74 };
75
76 let height = tipset.epoch();
77 let mut vm = VM::new(
78 ExecutionContext {
79 heaviest_tipset: tipset.shallow_clone(),
80 state_tree_root: state_cid,
81 epoch: height,
82 rand: Box::new(self.chain_rand(tipset.shallow_clone())),
83 base_fee: tipset.block_headers().first().parent_base_fee.clone(),
84 circ_supply: self.genesis_info().get_vm_circulating_supply(
85 height,
86 self.db(),
87 &state_cid,
88 )?,
89 chain_config: self.chain_config().shallow_clone(),
90 chain_index: self.chain_index().shallow_clone(),
91 timestamp: tipset.min_timestamp(),
92 },
93 &self.engine,
94 VMTrace::Traced,
95 )?;
96
97 let tipset_messages = self
98 .chain_store()
99 .messages_for_tipset(&tipset)
100 .map_err(|err| Error::Other(err.to_string()))?;
101
102 let prior_messsages = tipset_messages
103 .iter()
104 .filter(|ts_msg| ts_msg.message().from() == msg.from());
105
106 for m in prior_messsages {
107 vm.apply_message(m)?;
108 }
109
110 let state_cid = vm.flush()?;
113
114 let state = StateTree::new_from_root(self.db(), &state_cid)?;
115
116 let from_actor = state
117 .get_actor(&msg.from())?
118 .ok_or_else(|| anyhow::anyhow!("actor not found"))?;
119 msg.set_sequence(from_actor.sequence);
120
121 msg.gas_limit = IMPLICIT_MESSAGE_GAS_LIMIT as u64;
123
124 let (apply_ret, duration) = vm.apply_implicit_message(&msg)?;
125
126 let msg_cid = msg.cid();
127 Ok(ApiInvocResult {
128 msg,
129 msg_rct: Some(apply_ret.msg_receipt()),
130 msg_cid,
131 error: apply_ret.failure_info().unwrap_or_default(),
132 duration: duration.as_nanos().clamp(0, u128::from(u64::MAX)) as u64,
133 gas_cost: MessageGasCost::default(),
134 execution_trace: structured::parse_events(apply_ret.exec_trace()).unwrap_or_default(),
135 })
136 }
137
138 pub async fn call(
141 &self,
142 message: Arc<Message>,
143 tipset: Option<Tipset>,
144 ) -> Result<ApiInvocResult, Error> {
145 let this = self.shallow_clone();
146 tokio::task::spawn_blocking(move || this.call_blocking(&message, tipset)).await?
147 }
148
149 pub fn call_blocking(
151 &self,
152 message: &Message,
153 tipset: Option<Tipset>,
154 ) -> Result<ApiInvocResult, Error> {
155 self.call_raw_blocking(None, message, tipset)
156 }
157
158 pub async fn call_on_state(
161 &self,
162 state_cid: Cid,
163 message: Arc<Message>,
164 tipset: Option<Tipset>,
165 ) -> Result<ApiInvocResult, Error> {
166 let this = self.shallow_clone();
167 tokio::task::spawn_blocking(move || {
168 this.call_on_state_blocking(state_cid, &message, tipset)
169 })
170 .await?
171 }
172
173 pub fn call_on_state_blocking(
175 &self,
176 state_cid: Cid,
177 message: &Message,
178 tipset: Option<Tipset>,
179 ) -> Result<ApiInvocResult, Error> {
180 self.call_raw_blocking(Some(state_cid), message, tipset)
181 }
182
183 pub async fn apply_on_state_with_gas(
184 &self,
185 tipset: Option<Tipset>,
186 msg: Message,
187 vm_flush: VMFlush,
188 vm_trace: VMTrace,
189 ) -> anyhow::Result<(ApiInvocResult, Option<Cid>)> {
190 let ts = tipset.unwrap_or_else(|| self.heaviest_tipset());
191
192 let from_a = self.resolve_to_deterministic_address(msg.from, &ts).await?;
193 let chain_msg = ChainMessage::for_gas_estimation(msg.clone(), from_a.protocol());
194
195 let (apply_ret, duration, state_root) = self
196 .call_with_gas(chain_msg, Default::default(), Some(ts), vm_flush, vm_trace)
197 .await?;
198
199 let msg_rct = Some(apply_ret.msg_receipt());
200 let error = apply_ret.failure_info().unwrap_or_default();
201 Ok((
202 ApiInvocResult {
203 msg_cid: msg.cid(),
204 msg,
205 msg_rct,
206 error,
207 duration: duration.as_nanos().clamp(0, u128::from(u64::MAX)) as u64,
208 gas_cost: MessageGasCost::default(),
209 execution_trace: structured::parse_events(apply_ret.into_exec_trace())
210 .unwrap_or_default(),
211 },
212 state_root,
213 ))
214 }
215
216 pub async fn call_with_gas(
219 &self,
220 mut message: ChainMessage,
221 prior_messages: Arc<Vec<ChainMessage>>,
222 tipset: Option<Tipset>,
223 vm_flush: VMFlush,
224 vm_trace: VMTrace,
225 ) -> Result<(ApplyRet, Duration, Option<Cid>), Error> {
226 let ts = tipset.unwrap_or_else(|| self.heaviest_tipset());
227 let TipsetState { state_root, .. } = self
228 .load_tipset_state(&ts)
229 .await
230 .map_err(|e| Error::Other(format!("Could not load tipset state: {e:#}")))?;
231 let chain_rand = self.chain_rand(ts.clone());
232
233 let epoch = ts.epoch() + 1;
236 let this = self.shallow_clone();
237 tokio::task::spawn_blocking(move || {
238 let (ret, duration, state_cid) = stacker::grow(64 << 20, || -> anyhow::Result<_> {
241 let mut vm = VM::new(
242 ExecutionContext {
243 heaviest_tipset: ts.clone(),
244 state_tree_root: state_root,
245 epoch,
246 rand: Box::new(chain_rand),
247 base_fee: ts.block_headers().first().parent_base_fee.clone(),
248 circ_supply: this.genesis_info().get_vm_circulating_supply(
249 epoch,
250 this.chain_index().db(),
251 &state_root,
252 )?,
253 chain_config: this.chain_config().shallow_clone(),
254 chain_index: this.chain_index().shallow_clone(),
255 timestamp: ts.min_timestamp(),
256 },
257 &this.engine,
258 vm_trace,
259 )?;
260
261 for msg in prior_messages.iter() {
262 vm.apply_message(msg)?;
263 }
264
265 let from_actor = vm
266 .get_actor(&message.from())
267 .map_err(|e| Error::Other(format!("Could not get actor from state: {e:#}")))?
268 .ok_or_else(|| Error::Other("cant find actor in state tree".to_string()))?;
269
270 message.set_sequence(from_actor.sequence);
271 let (ret, duration) = vm.apply_message(&message)?;
272 let state_root = match vm_flush {
273 VMFlush::Flush => Some(vm.flush()?),
274 VMFlush::Skip => None,
275 };
276 Ok((ret, duration, state_root))
277 })?;
278
279 Ok((ret, duration, state_cid))
280 })
281 .await?
282 }
283}