1mod resolve;
5
6use std::{
7 fmt::Write as FmtWrite,
8 io::{Write, stdout},
9 sync::Arc,
10};
11
12use crate::shim::actors::{
13 account::State as AccountState, cron::State as CronState, datacap::State as DatacapState,
14 evm::State as EvmState, init::State as InitState, market::State as MarketState,
15 miner::State as MinerState, multisig::State as MultiSigState, power::State as PowerState,
16 reward::State as RewardState, system::State as SystemState,
17};
18use crate::{
19 lotus_json::HasLotusJson as _,
20 shim::{
21 actors::state_load::*,
22 address::Address,
23 state_tree::{ActorState, StateTree},
24 },
25};
26use ahash::HashMap;
27use cid::Cid;
28use colored::*;
29use fvm_ipld_blockstore::Blockstore;
30use ipld_core::ipld::Ipld;
31use itertools::Itertools as _;
32use resolve::resolve_cids_recursive;
33use serde::{Deserialize, Serialize};
34use similar::{ChangeTag, TextDiff};
35
36#[derive(Serialize, Deserialize)]
37struct ActorStateResolved {
38 #[serde(with = "crate::lotus_json")]
39 code: Cid,
40 sequence: u64,
41 balance: String,
42 #[serde(with = "crate::lotus_json")]
43 state: Ipld,
44}
45
46fn actor_to_resolved(
47 bs: &impl Blockstore,
48 actor: &ActorState,
49 depth: Option<u64>,
50) -> ActorStateResolved {
51 let resolved =
52 resolve_cids_recursive(bs, &actor.state, depth).unwrap_or(Ipld::Link(actor.state));
53 ActorStateResolved {
54 state: resolved,
55 code: actor.code,
56 balance: actor.balance.to_string(),
57 sequence: actor.sequence,
58 }
59}
60
61fn root_to_state_map<BS: Blockstore>(
62 bs: &Arc<BS>,
63 root: &Cid,
64) -> Result<HashMap<Address, ActorState>, anyhow::Error> {
65 let mut actors = HashMap::default();
66 let state_tree = StateTree::new_from_root(bs.clone(), root)?;
67 state_tree.for_each(|addr: Address, actor: &ActorState| {
68 actors.insert(addr, actor.clone());
69 Ok(())
70 })?;
71
72 Ok(actors)
73}
74
75fn try_print_actor_states<BS: Blockstore>(
80 bs: &Arc<BS>,
81 root: &Cid,
82 expected_root: &Cid,
83 depth: Option<u64>,
84) -> Result<(), anyhow::Error> {
85 let mut e_state = root_to_state_map(bs, expected_root)?;
88
89 let state_tree = StateTree::new_from_root(bs.clone(), root)?;
91
92 state_tree.for_each(|addr: Address, actor| {
93 if let Some(other) = e_state.remove(&addr) {
94 if &other != actor {
95 const COMMA: &str = ",";
96 let calc_pp = pp_actor_state(bs, actor, depth)?;
97 let expected_pp = pp_actor_state(bs, &other, depth)?;
98 let expected = expected_pp
99 .split(COMMA)
100 .map(|s| s.trim_start_matches('\n'))
101 .collect_vec();
102 let calculated = calc_pp
103 .split(COMMA)
104 .map(|s| s.trim_start_matches('\n'))
105 .collect_vec();
106 let diffs = TextDiff::from_slices(&expected, &calculated);
107 let stdout = stdout();
108 let mut handle = stdout.lock();
109 writeln!(handle, "Address {addr} changed: ")?;
110 print_diffs(&mut handle, diffs)?;
111 }
112 } else {
113 let calc_pp = pp_actor_state(bs, actor, depth)?;
114 println!("{}", format!("+ Address {addr}:\n{calc_pp}").green());
116 }
117
118 Ok(())
119 })?;
120
121 for (addr, state) in e_state.into_iter() {
123 let expected_json = serde_json::to_string_pretty(&actor_to_resolved(bs, &state, depth))?;
124 println!("{}", format!("- Address {addr}:\n{expected_json}").red())
125 }
126
127 Ok(())
128}
129
130fn pp_actor_state(
131 bs: &impl Blockstore,
132 actor_state: &ActorState,
133 depth: Option<u64>,
134) -> Result<String, anyhow::Error> {
135 let mut buffer = String::new();
136 writeln!(&mut buffer, "{actor_state:?}")?;
137 if let Ok(miner_state) = MinerState::load(bs, actor_state.code, actor_state.state) {
138 write!(&mut buffer, "{miner_state:?}")?;
139 return Ok(buffer);
140 }
141 if let Ok(cron_state) = CronState::load(bs, actor_state.code, actor_state.state) {
142 write!(&mut buffer, "{cron_state:?}")?;
143 return Ok(buffer);
144 }
145 if let Ok(account_state) = AccountState::load(bs, actor_state.code, actor_state.state) {
146 write!(&mut buffer, "{account_state:?}")?;
147 return Ok(buffer);
148 }
149 if let Ok(power_state) = PowerState::load(bs, actor_state.code, actor_state.state) {
150 write!(&mut buffer, "{power_state:?}")?;
151 return Ok(buffer);
152 }
153 if let Ok(init_state) = InitState::load(bs, actor_state.code, actor_state.state) {
154 write!(&mut buffer, "{init_state:?}")?;
155 return Ok(buffer);
156 }
157 if let Ok(reward_state) = RewardState::load(bs, actor_state.code, actor_state.state) {
158 write!(&mut buffer, "{reward_state:?}")?;
159 return Ok(buffer);
160 }
161 if let Ok(system_state) = SystemState::load(bs, actor_state.code, actor_state.state) {
162 write!(&mut buffer, "{system_state:?}")?;
163 return Ok(buffer);
164 }
165 if let Ok(multi_sig_state) = MultiSigState::load(bs, actor_state.code, actor_state.state) {
166 write!(&mut buffer, "{multi_sig_state:?}")?;
167 return Ok(buffer);
168 }
169 if let Ok(market_state) = MarketState::load(bs, actor_state.code, actor_state.state) {
170 write!(&mut buffer, "{market_state:?}")?;
171 return Ok(buffer);
172 }
173 if let Ok(datacap_state) = DatacapState::load(bs, actor_state.code, actor_state.state) {
174 write!(&mut buffer, "{datacap_state:?}")?;
175 return Ok(buffer);
176 }
177 if let Ok(evm_state) = EvmState::load(bs, actor_state.code, actor_state.state) {
178 write!(&mut buffer, "{evm_state:?}")?;
179 return Ok(buffer);
180 }
181
182 let resolved = actor_to_resolved(bs, actor_state, depth);
183 buffer = serde_json::to_string_pretty(&resolved)?;
184 Ok(buffer)
185}
186
187fn print_diffs(handle: &mut impl Write, diffs: TextDiff<str>) -> std::io::Result<()> {
188 for op in diffs.ops() {
189 for change in diffs.iter_changes(op) {
190 match change.tag() {
191 ChangeTag::Delete => writeln!(handle, "{}", format!("-{}", change.value()).red())?,
192 ChangeTag::Insert => {
193 writeln!(handle, "{}", format!("+{}", change.value()).green())?
194 }
195 ChangeTag::Equal => writeln!(handle, " {}", change.value())?,
196 };
197 }
198 }
199 Ok(())
200}
201
202pub fn print_state_diff<BS>(
205 bs: &Arc<BS>,
206 root: &Cid,
207 expected_root: &Cid,
208 depth: Option<u64>,
209) -> Result<(), anyhow::Error>
210where
211 BS: Blockstore,
212{
213 if let Err(e) = try_print_actor_states(bs, root, expected_root, depth) {
214 println!("Could not resolve actor states: {e}\nUsing default resolution:");
215 let expected = resolve_cids_recursive(bs, expected_root, depth)?;
216 let actual = resolve_cids_recursive(bs, root, depth)?;
217
218 let expected_json = expected.into_lotus_json_string_pretty()?;
219 let actual_json = actual.into_lotus_json_string_pretty()?;
220
221 let diffs = TextDiff::from_lines(&expected_json, &actual_json);
222
223 let stdout = stdout();
224 let mut handle = stdout.lock();
225 print_diffs(&mut handle, diffs)?
226 }
227
228 Ok(())
229}
230
231#[cfg(test)]
232mod tests {
233 use crate::db::MemoryDB;
234 use crate::shim::{address::Address, econ::TokenAmount, state_tree::ActorState};
235 use crate::utils::db::CborStoreExt;
236 use cid::Cid;
237 use fil_actor_account_state::v10::State as AccountState;
238 use fvm_ipld_blockstore::Blockstore;
239
240 use super::pp_actor_state;
241
242 fn mk_account_v10(db: &impl Blockstore, account: &AccountState) -> ActorState {
243 let account_cid =
245 Cid::try_from("bafk2bzaceampw4romta75hyz5p4cqriypmpbgnkxncgxgqn6zptv5lsp2w2bo")
246 .unwrap();
247 let actor_state_cid = db.put_cbor_default(&account).unwrap();
248 ActorState::new(
249 account_cid,
250 actor_state_cid,
251 TokenAmount::from_atto(0),
252 0,
253 None,
254 )
255 }
256
257 #[test]
259 fn correctly_pretty_print_account_actor_state() {
260 let db = MemoryDB::default();
261
262 let account_state = AccountState {
263 address: Address::new_id(0xdeadbeef).into(),
264 };
265 let state = mk_account_v10(&db, &account_state);
266
267 let pretty = pp_actor_state(&db, &state, None).unwrap();
268
269 assert_eq!(
270 pretty,
271 "ActorState(\
272 ActorState { \
273 code: Cid(bafk2bzaceampw4romta75hyz5p4cqriypmpbgnkxncgxgqn6zptv5lsp2w2bo), \
274 state: Cid(bafy2bzaceaiws3hdhmfyxyfjzmbaxv5aw6eywwbipeae4n5jjg5smmfxsaeic), \
275 sequence: 0, balance: TokenAmount(0.0), delegated_address: None })\n\
276 V10(State { address: Address { payload: ID(3735928559) } })"
277 );
278 }
279
280 #[test]
283 fn check_json_fallback_if_unknown_actor() {
284 let db = MemoryDB::default();
285
286 let account_state = AccountState {
287 address: Address::new_id(0xdeadbeef).into(),
288 };
289 let mut state = mk_account_v10(&db, &account_state);
290 state.code = Cid::default(); let pretty = pp_actor_state(&db, &state, None).unwrap();
293
294 assert_eq!(
295 pretty,
296 "{
297 \"code\": {
298 \"/\": \"baeaaaaa\"
299 },
300 \"sequence\": 0,
301 \"balance\": \"0.0\",
302 \"state\": [
303 {
304 \"/\": {
305 \"bytes\": \"mAO/9tvUN\"
306 }
307 }
308 ]
309}"
310 );
311 }
312}