1use super::circulating_supply::GenesisInfo;
5use super::utils::structured;
6use super::*;
7use crate::db::EthMappingsStore;
8use crate::interpreter::{ExecutionContext, IMPLICIT_MESSAGE_GAS_LIMIT, VM, VMTrace};
9use crate::message::{MessageRead as _, MessageReadWrite as _, SignedMessage};
10use crate::rpc::state::{ApiInvocResult, InvocResult, MessageGasCost};
11use crate::shim::address::Protocol;
12use crate::shim::crypto::{Signature, SignatureType};
13use crate::shim::executor::ApplyRet;
14use crate::shim::message::Message;
15use crate::utils::ShallowClone as _;
16use fvm_shared4::crypto::signature::SECP_SIG_LEN;
17use std::time::Duration;
18use tracing::instrument;
19
20impl<DB> StateManager<DB>
21where
22 DB: Blockstore + Send + Sync + 'static,
23{
24 #[instrument(skip(self, rand))]
25 fn call_raw(
26 &self,
27 state_cid: Option<Cid>,
28 msg: &Message,
29 rand: ChainRand<DB>,
30 tipset: &Tipset,
31 ) -> Result<ApiInvocResult, Error>
32 where
33 DB: EthMappingsStore,
34 {
35 let mut msg = msg.clone();
36
37 let state_cid = state_cid.unwrap_or(*tipset.parent_state());
38
39 let tipset_messages = self
40 .chain_store()
41 .messages_for_tipset(tipset)
42 .map_err(|err| Error::Other(err.to_string()))?;
43
44 let prior_messsages = tipset_messages
45 .iter()
46 .filter(|ts_msg| ts_msg.message().from() == msg.from());
47
48 let height = tipset.epoch();
51 let genesis_info = GenesisInfo::from_chain_config(self.chain_config().clone());
52 let mut vm = VM::new(
53 ExecutionContext {
54 heaviest_tipset: tipset.shallow_clone(),
55 state_tree_root: state_cid,
56 epoch: height,
57 rand: Box::new(rand),
58 base_fee: tipset.block_headers().first().parent_base_fee.clone(),
59 circ_supply: genesis_info.get_vm_circulating_supply(
60 height,
61 self.blockstore(),
62 &state_cid,
63 )?,
64 chain_config: self.chain_config().shallow_clone(),
65 chain_index: self.chain_index().shallow_clone(),
66 timestamp: tipset.min_timestamp(),
67 },
68 &self.engine,
69 VMTrace::Traced,
70 )?;
71
72 for m in prior_messsages {
73 vm.apply_message(m)?;
74 }
75
76 let state_cid = vm.flush()?;
79
80 let state = StateTree::new_from_root(self.blockstore_owned(), &state_cid)?;
81
82 let from_actor = state
83 .get_actor(&msg.from())?
84 .ok_or_else(|| anyhow::anyhow!("actor not found"))?;
85 msg.set_sequence(from_actor.sequence);
86
87 let mut msg = msg.clone();
89 msg.gas_limit = IMPLICIT_MESSAGE_GAS_LIMIT as u64;
90
91 let (apply_ret, duration) = vm.apply_implicit_message(&msg)?;
92
93 Ok(ApiInvocResult {
94 msg: msg.clone(),
95 msg_rct: Some(apply_ret.msg_receipt()),
96 msg_cid: msg.cid(),
97 error: apply_ret.failure_info().unwrap_or_default(),
98 duration: duration.as_nanos().clamp(0, u128::from(u64::MAX)) as u64,
99 gas_cost: MessageGasCost::default(),
100 execution_trace: structured::parse_events(apply_ret.exec_trace()).unwrap_or_default(),
101 })
102 }
103
104 pub fn call(&self, message: &Message, tipset: Option<Tipset>) -> Result<ApiInvocResult, Error>
107 where
108 DB: EthMappingsStore,
109 {
110 let ts = tipset.unwrap_or_else(|| self.heaviest_tipset());
111 let chain_rand = self.chain_rand(ts.shallow_clone());
112 self.call_raw(None, message, chain_rand, &ts)
113 }
114
115 pub fn call_on_state(
118 &self,
119 state_cid: Cid,
120 message: &Message,
121 tipset: Option<Tipset>,
122 ) -> Result<ApiInvocResult, Error>
123 where
124 DB: EthMappingsStore,
125 {
126 let ts = tipset.unwrap_or_else(|| self.cs.heaviest_tipset());
127 let chain_rand = self.chain_rand(ts.shallow_clone());
128 self.call_raw(Some(state_cid), message, chain_rand, &ts)
129 }
130
131 pub async fn apply_on_state_with_gas(
132 self: &Arc<Self>,
133 tipset: Option<Tipset>,
134 msg: Message,
135 vm_flush: VMFlush,
136 ) -> anyhow::Result<(ApiInvocResult, Option<Cid>)>
137 where
138 DB: EthMappingsStore,
139 {
140 let ts = tipset.unwrap_or_else(|| self.heaviest_tipset());
141
142 let from_a = self.resolve_to_key_addr(&msg.from, &ts).await?;
143
144 let mut chain_msg = match from_a.protocol() {
148 Protocol::Secp256k1 => SignedMessage::new_unchecked(
149 msg.clone(),
150 Signature::new_secp256k1(vec![0; SECP_SIG_LEN]),
151 )
152 .into(),
153 Protocol::Delegated => SignedMessage::new_unchecked(
154 msg.clone(),
155 Signature::new(SignatureType::Delegated, vec![0; SECP_SIG_LEN]),
158 )
159 .into(),
160 _ => msg.clone().into(),
161 };
162
163 let (_invoc_res, apply_ret, duration, state_root) = self
164 .call_with_gas(&mut chain_msg, &[], Some(ts), vm_flush)
165 .await?;
166
167 Ok((
168 ApiInvocResult {
169 msg_cid: msg.cid(),
170 msg,
171 msg_rct: Some(apply_ret.msg_receipt()),
172 error: apply_ret.failure_info().unwrap_or_default(),
173 duration: duration.as_nanos().clamp(0, u128::from(u64::MAX)) as u64,
174 gas_cost: MessageGasCost::default(),
175 execution_trace: structured::parse_events(apply_ret.exec_trace())
176 .unwrap_or_default(),
177 },
178 state_root,
179 ))
180 }
181
182 pub async fn call_with_gas(
185 self: &Arc<Self>,
186 message: &mut ChainMessage,
187 prior_messages: &[ChainMessage],
188 tipset: Option<Tipset>,
189 vm_flush: VMFlush,
190 ) -> Result<(InvocResult, ApplyRet, Duration, Option<Cid>), Error>
191 where
192 DB: EthMappingsStore,
193 {
194 let ts = tipset.unwrap_or_else(|| self.heaviest_tipset());
195 let TipsetState { state_root, .. } = self
196 .load_tipset_state(&ts)
197 .await
198 .map_err(|e| Error::Other(format!("Could not load tipset state: {e:#}")))?;
199 let chain_rand = self.chain_rand(ts.clone());
200
201 let epoch = ts.epoch() + 1;
204 let genesis_info = GenesisInfo::from_chain_config(self.chain_config().clone());
205 let (ret, duration, state_cid) = stacker::grow(64 << 20, || -> anyhow::Result<_> {
208 let mut vm = VM::new(
209 ExecutionContext {
210 heaviest_tipset: ts.clone(),
211 state_tree_root: state_root,
212 epoch,
213 rand: Box::new(chain_rand),
214 base_fee: ts.block_headers().first().parent_base_fee.clone(),
215 circ_supply: genesis_info.get_vm_circulating_supply(
216 epoch,
217 self.blockstore(),
218 &state_root,
219 )?,
220 chain_config: self.chain_config().shallow_clone(),
221 chain_index: self.chain_index().shallow_clone(),
222 timestamp: ts.min_timestamp(),
223 },
224 &self.engine,
225 VMTrace::NotTraced,
226 )?;
227
228 for msg in prior_messages {
229 vm.apply_message(msg)?;
230 }
231 let from_actor = vm
232 .get_actor(&message.from())
233 .map_err(|e| Error::Other(format!("Could not get actor from state: {e:#}")))?
234 .ok_or_else(|| Error::Other("cant find actor in state tree".to_string()))?;
235
236 message.set_sequence(from_actor.sequence);
237 let (ret, duration) = vm.apply_message(message)?;
238 let state_root = match vm_flush {
239 VMFlush::Flush => Some(vm.flush()?),
240 VMFlush::Skip => None,
241 };
242 Ok((ret, duration, state_root))
243 })?;
244
245 Ok((
246 InvocResult::new(message.message().clone(), &ret),
247 ret,
248 duration,
249 state_cid,
250 ))
251 }
252}