1use super::super::EthBigInt;
10use super::super::types::{EthAddress, EthHash};
11use super::super::utils::ActorStateEthExt as _;
12use super::types::{AccountDiff, ChangedType, Delta, StateDiff};
13use super::utils::{ZERO_HASH, u256_to_eth_hash};
14use crate::prelude::*;
15use crate::shim::actors::{EVMActorStateLoad as _, evm, is_evm_actor};
16use crate::shim::state_tree::{ActorState, StateTree};
17use ahash::{HashMap, HashSet};
18use fil_actor_evm_state::evm_shared::v17::uints::U256;
19use fvm_ipld_kamt::{AsHashedKey, Config as KamtConfig, HashedKey, Kamt};
20use std::borrow::Cow;
21use std::collections::BTreeMap;
22use tracing::debug;
23
24fn evm_kamt_config() -> KamtConfig {
27 KamtConfig {
28 bit_width: 5, min_data_depth: 0, max_array_width: 1, }
32}
33
34struct EvmStateHashAlgorithm;
37
38impl AsHashedKey<U256, 32> for EvmStateHashAlgorithm {
39 fn as_hashed_key(key: &U256) -> Cow<'_, HashedKey<32>> {
40 Cow::Owned(key.to_big_endian())
41 }
42}
43
44type EvmStorageKamt<BS> = Kamt<BS, U256, U256, EvmStateHashAlgorithm>;
46
47pub fn build_state_diff<S: Blockstore, T: Blockstore>(
49 store: &S,
50 pre_state: &StateTree<T>,
51 post_state: &StateTree<T>,
52 touched_addresses: &HashSet<EthAddress>,
53) -> anyhow::Result<StateDiff> {
54 let mut state_diff = StateDiff::new();
55
56 for eth_addr in touched_addresses {
57 let fil_addr = eth_addr.to_filecoin_address()?;
58
59 let pre_actor = pre_state
61 .get_actor(&fil_addr)
62 .context("failed to get actor state")?;
63
64 let post_actor = post_state
65 .get_actor(&fil_addr)
66 .context("failed to get actor state")?;
67
68 let account_diff = build_account_diff(store, pre_actor.as_ref(), post_actor.as_ref())?;
69
70 state_diff.insert_if_changed(*eth_addr, account_diff);
72 }
73
74 Ok(state_diff)
75}
76
77fn build_account_diff<DB: Blockstore>(
79 store: &DB,
80 pre_actor: Option<&ActorState>,
81 post_actor: Option<&ActorState>,
82) -> anyhow::Result<AccountDiff> {
83 let mut diff = AccountDiff::default();
84
85 let pre_balance = pre_actor.map(|a| EthBigInt::from(a.balance.atto()));
87 let post_balance = post_actor.map(|a| EthBigInt::from(a.balance.atto()));
88 diff.balance = Delta::from_comparison(pre_balance, post_balance);
89
90 let pre_nonce = pre_actor.map(|a| a.eth_nonce(store)).transpose()?;
92 let post_nonce = post_actor.map(|a| a.eth_nonce(store)).transpose()?;
93 diff.nonce = Delta::from_comparison(pre_nonce, post_nonce);
94
95 let pre_code = pre_actor
97 .map(|a| a.eth_bytecode(store))
98 .transpose()?
99 .flatten();
100 let post_code = post_actor
101 .map(|a| a.eth_bytecode(store))
102 .transpose()?
103 .flatten();
104 diff.code = Delta::from_comparison(pre_code, post_code);
105
106 diff.storage = diff_evm_storage_for_actors(store, pre_actor, post_actor)?;
108
109 Ok(diff)
110}
111
112fn diff_evm_storage_for_actors<DB: Blockstore>(
120 store: &DB,
121 pre_actor: Option<&ActorState>,
122 post_actor: Option<&ActorState>,
123) -> anyhow::Result<BTreeMap<EthHash, Delta<EthHash>>> {
124 let pre_is_evm = pre_actor.is_some_and(|a| is_evm_actor(&a.code));
125 let post_is_evm = post_actor.is_some_and(|a| is_evm_actor(&a.code));
126
127 let pre_entries = extract_evm_storage_entries(store, pre_actor);
129 let post_entries = extract_evm_storage_entries(store, post_actor);
130
131 if pre_entries.is_empty() && post_entries.is_empty() {
133 return Ok(BTreeMap::new());
134 }
135
136 let mut diff = BTreeMap::new();
137
138 match (pre_is_evm, post_is_evm) {
139 (false, true) => {
140 for (key_bytes, value) in &post_entries {
141 let key_hash = EthHash(ethereum_types::H256(*key_bytes));
142 diff.insert(key_hash, Delta::Added(u256_to_eth_hash(value)));
143 }
144 }
145 (true, false) => {
146 for (key_bytes, value) in &pre_entries {
147 let key_hash = EthHash(ethereum_types::H256(*key_bytes));
148 diff.insert(key_hash, Delta::Removed(u256_to_eth_hash(value)));
149 }
150 }
151 (true, true) => {
152 for (key_bytes, pre_value) in &pre_entries {
153 let key_hash = EthHash(ethereum_types::H256(*key_bytes));
154 let pre_hash = u256_to_eth_hash(pre_value);
155
156 match post_entries.get(key_bytes) {
157 Some(post_value) if pre_value != post_value => {
158 diff.insert(
160 key_hash,
161 Delta::Changed(ChangedType {
162 from: pre_hash,
163 to: u256_to_eth_hash(post_value),
164 }),
165 );
166 }
167 Some(_) => {
168 }
170 None => {
171 diff.insert(
173 key_hash,
174 Delta::Changed(ChangedType {
175 from: pre_hash,
176 to: ZERO_HASH,
177 }),
178 );
179 }
180 }
181 }
182
183 for (key_bytes, post_value) in &post_entries {
185 if !pre_entries.contains_key(key_bytes) {
186 let key_hash = EthHash(ethereum_types::H256(*key_bytes));
187 diff.insert(
188 key_hash,
189 Delta::Changed(ChangedType {
190 from: ZERO_HASH,
191 to: u256_to_eth_hash(post_value),
192 }),
193 );
194 }
195 }
196 }
197 (false, false) => {}
199 }
200
201 Ok(diff)
202}
203
204pub fn extract_evm_storage_entries<DB: Blockstore>(
207 store: &DB,
208 actor: Option<&ActorState>,
209) -> HashMap<[u8; 32], U256> {
210 let actor = match actor {
211 Some(a) if is_evm_actor(&a.code) => a,
212 _ => return HashMap::default(),
213 };
214
215 let evm_state = match evm::State::load(store, actor.code, actor.state) {
216 Ok(state) => state,
217 Err(e) => {
218 debug!("failed to load EVM state for storage extraction: {e:#}");
219 return HashMap::default();
220 }
221 };
222
223 let storage_cid = evm_state.contract_state();
224 let config = evm_kamt_config();
225
226 let kamt: EvmStorageKamt<&DB> = match Kamt::load_with_config(&storage_cid, store, config) {
227 Ok(k) => k,
228 Err(e) => {
229 debug!("failed to load storage KAMT: {e}");
230 return HashMap::default();
231 }
232 };
233
234 let mut entries = HashMap::default();
235 if let Err(e) = kamt.for_each(|key, value| {
236 entries.insert(key.to_big_endian(), *value);
237 Ok(())
238 }) {
239 debug!("failed to iterate storage KAMT: {e}");
240 return HashMap::default();
241 }
242
243 entries
244}
245
246pub fn diff_entry_keys(
248 pre_entries: &HashMap<[u8; 32], U256>,
249 post_entries: &HashMap<[u8; 32], U256>,
250) -> HashSet<[u8; 32]> {
251 let mut changed = HashSet::default();
252
253 for (k, v) in pre_entries {
254 match post_entries.get(k) {
255 Some(pv) if pv == v => {} _ => {
257 changed.insert(*k);
258 }
259 }
260 }
261
262 for k in post_entries.keys() {
263 if !pre_entries.contains_key(k) {
264 changed.insert(*k);
265 }
266 }
267
268 changed
269}
270
271#[cfg(test)]
272mod tests {
273 use super::super::test_helpers::*;
274 use super::*;
275 use crate::db::MemoryDB;
276 use crate::rpc::eth::EthUint64;
277 use crate::rpc::eth::types::EthBytes;
278 use crate::shim::address::Address as FilecoinAddress;
279 use crate::shim::state_tree::StateTreeVersion;
280
281 #[test]
282 fn test_build_state_diff_empty_touched_addresses() {
283 let trees = TestStateTrees::new().unwrap();
284 let touched_addresses = HashSet::new();
285
286 let state_diff = trees.build_diff(&touched_addresses).unwrap();
287
288 assert!(state_diff.0.is_empty());
290 }
291
292 #[test]
293 fn test_build_state_diff_nonexistent_address() {
294 let trees = TestStateTrees::new().unwrap();
295 let mut touched_addresses = HashSet::new();
296 touched_addresses.insert(create_masked_id_eth_address(9999));
297
298 let state_diff = trees.build_diff(&touched_addresses).unwrap();
299
300 assert!(state_diff.0.is_empty());
302 }
303
304 #[test]
305 fn test_build_state_diff_balance_increase() {
306 let actor_id = 1001u64;
307 let pre_actor = create_test_actor(1000, 5);
308 let post_actor = create_test_actor(2000, 5);
309 let trees = TestStateTrees::with_changed_actor(actor_id, pre_actor, post_actor).unwrap();
310
311 let mut touched_addresses = HashSet::new();
312 touched_addresses.insert(create_masked_id_eth_address(actor_id));
313
314 let state_diff = trees.build_diff(&touched_addresses).unwrap();
315
316 assert_eq!(state_diff.0.len(), 1);
317 let eth_addr = create_masked_id_eth_address(actor_id);
318 let diff = state_diff.0.get(ð_addr).unwrap();
319 match &diff.balance {
320 Delta::Changed(change) => {
321 assert_eq!(change.from, EthBigInt::from(1000));
322 assert_eq!(change.to, EthBigInt::from(2000));
323 }
324 _ => panic!("Expected Delta::Changed for balance"),
325 }
326 assert!(diff.nonce.is_unchanged());
327 }
328
329 #[test]
330 fn test_build_state_diff_balance_decrease() {
331 let actor_id = 1002u64;
332 let pre_actor = create_test_actor(5000, 10);
333 let post_actor = create_test_actor(3000, 10);
334 let trees = TestStateTrees::with_changed_actor(actor_id, pre_actor, post_actor).unwrap();
335
336 let mut touched_addresses = HashSet::new();
337 touched_addresses.insert(create_masked_id_eth_address(actor_id));
338
339 let state_diff = trees.build_diff(&touched_addresses).unwrap();
340
341 let eth_addr = create_masked_id_eth_address(actor_id);
342 let diff = state_diff.0.get(ð_addr).unwrap();
343 match &diff.balance {
344 Delta::Changed(change) => {
345 assert_eq!(change.from, EthBigInt::from(5000));
346 assert_eq!(change.to, EthBigInt::from(3000));
347 }
348 _ => panic!("Expected Delta::Changed for balance"),
349 }
350 assert!(diff.nonce.is_unchanged());
351 }
352
353 #[test]
354 fn test_build_state_diff_nonce_increment() {
355 let actor_id = 1003u64;
356 let pre_actor = create_test_actor(1000, 5);
357 let post_actor = create_test_actor(1000, 6);
358 let trees = TestStateTrees::with_changed_actor(actor_id, pre_actor, post_actor).unwrap();
359
360 let mut touched_addresses = HashSet::new();
361 touched_addresses.insert(create_masked_id_eth_address(actor_id));
362
363 let state_diff = trees.build_diff(&touched_addresses).unwrap();
364
365 let eth_addr = create_masked_id_eth_address(actor_id);
366 let diff = state_diff.0.get(ð_addr).unwrap();
367 assert!(diff.balance.is_unchanged());
368 match &diff.nonce {
369 Delta::Changed(change) => {
370 assert_eq!(change.from.0, 5);
371 assert_eq!(change.to.0, 6);
372 }
373 _ => panic!("Expected Delta::Changed for nonce"),
374 }
375 }
376
377 #[test]
378 fn test_build_state_diff_both_balance_and_nonce_change() {
379 let actor_id = 1004u64;
380 let pre_actor = create_test_actor(10000, 100);
381 let post_actor = create_test_actor(9000, 101);
382 let trees = TestStateTrees::with_changed_actor(actor_id, pre_actor, post_actor).unwrap();
383
384 let mut touched_addresses = HashSet::new();
385 touched_addresses.insert(create_masked_id_eth_address(actor_id));
386
387 let state_diff = trees.build_diff(&touched_addresses).unwrap();
388
389 let eth_addr = create_masked_id_eth_address(actor_id);
390 let diff = state_diff.0.get(ð_addr).unwrap();
391 match &diff.balance {
392 Delta::Changed(change) => {
393 assert_eq!(change.from, EthBigInt::from(10000));
394 assert_eq!(change.to, EthBigInt::from(9000));
395 }
396 _ => panic!("Expected Delta::Changed for balance"),
397 }
398 match &diff.nonce {
399 Delta::Changed(change) => {
400 assert_eq!(change.from.0, 100);
401 assert_eq!(change.to.0, 101);
402 }
403 _ => panic!("Expected Delta::Changed for nonce"),
404 }
405 }
406
407 #[test]
408 fn test_build_state_diff_account_creation() {
409 let actor_id = 1005u64;
410 let post_actor = create_test_actor(5000, 0);
411 let trees = TestStateTrees::with_created_actor(actor_id, post_actor).unwrap();
412
413 let mut touched_addresses = HashSet::new();
414 touched_addresses.insert(create_masked_id_eth_address(actor_id));
415
416 let state_diff = trees.build_diff(&touched_addresses).unwrap();
417
418 let eth_addr = create_masked_id_eth_address(actor_id);
419 let diff = state_diff.0.get(ð_addr).unwrap();
420 match &diff.balance {
421 Delta::Added(balance) => {
422 assert_eq!(balance, &EthBigInt::from(5000));
423 }
424 _ => panic!("Expected Delta::Added for balance"),
425 }
426 match &diff.nonce {
427 Delta::Added(nonce) => {
428 assert_eq!(nonce.0, 0);
429 }
430 _ => panic!("Expected Delta::Added for nonce"),
431 }
432 }
433
434 #[test]
435 fn test_build_state_diff_account_deletion() {
436 let actor_id = 1006u64;
437 let pre_actor = create_test_actor(3000, 10);
438 let trees = TestStateTrees::with_deleted_actor(actor_id, pre_actor).unwrap();
439
440 let mut touched_addresses = HashSet::new();
441 touched_addresses.insert(create_masked_id_eth_address(actor_id));
442
443 let state_diff = trees.build_diff(&touched_addresses).unwrap();
444
445 let eth_addr = create_masked_id_eth_address(actor_id);
446 let diff = state_diff.0.get(ð_addr).unwrap();
447 match &diff.balance {
448 Delta::Removed(balance) => {
449 assert_eq!(balance, &EthBigInt::from(3000));
450 }
451 _ => panic!("Expected Delta::Removed for balance"),
452 }
453 match &diff.nonce {
454 Delta::Removed(nonce) => {
455 assert_eq!(nonce.0, 10);
456 }
457 _ => panic!("Expected Delta::Removed for nonce"),
458 }
459 }
460
461 #[test]
462 fn test_build_state_diff_multiple_addresses() {
463 let store = Arc::new(MemoryDB::default());
464 let mut pre_state = StateTree::new(&store, StateTreeVersion::V5).unwrap();
465 let mut post_state = StateTree::new(&store, StateTreeVersion::V5).unwrap();
466
467 let addr1 = FilecoinAddress::new_id(2001);
469 pre_state
470 .set_actor(&addr1, create_test_actor(1000, 0))
471 .unwrap();
472 post_state
473 .set_actor(&addr1, create_test_actor(2000, 0))
474 .unwrap();
475
476 let addr2 = FilecoinAddress::new_id(2002);
478 pre_state
479 .set_actor(&addr2, create_test_actor(500, 5))
480 .unwrap();
481 post_state
482 .set_actor(&addr2, create_test_actor(500, 6))
483 .unwrap();
484
485 let addr3 = FilecoinAddress::new_id(2003);
487 pre_state
488 .set_actor(&addr3, create_test_actor(100, 1))
489 .unwrap();
490 post_state
491 .set_actor(&addr3, create_test_actor(100, 1))
492 .unwrap();
493
494 let mut touched_addresses = HashSet::new();
495 touched_addresses.insert(create_masked_id_eth_address(2001));
496 touched_addresses.insert(create_masked_id_eth_address(2002));
497 touched_addresses.insert(create_masked_id_eth_address(2003));
498
499 let state_diff =
500 build_state_diff(store.as_ref(), &pre_state, &post_state, &touched_addresses).unwrap();
501
502 assert_eq!(state_diff.0.len(), 2);
503 assert!(
504 state_diff
505 .0
506 .contains_key(&create_masked_id_eth_address(2001))
507 );
508 assert!(
509 state_diff
510 .0
511 .contains_key(&create_masked_id_eth_address(2002))
512 );
513 assert!(
514 !state_diff
515 .0
516 .contains_key(&create_masked_id_eth_address(2003))
517 );
518 }
519
520 #[test]
521 fn test_build_state_diff_evm_actor_scenarios() {
522 struct TestCase {
523 name: &'static str,
524 pre: Option<(u64, u64, Option<&'static [u8]>)>, post: Option<(u64, u64, Option<&'static [u8]>)>,
526 expected_balance: Delta<EthBigInt>,
527 expected_nonce: Delta<EthUint64>,
528 expected_code: Delta<EthBytes>,
529 }
530
531 let bytecode1: &[u8] = &[0x60, 0x80, 0x60, 0x40, 0x52];
532 let bytecode2: &[u8] = &[0x60, 0x80, 0x60, 0x40, 0x52, 0x00];
533
534 let cases = vec![
535 TestCase {
536 name: "No change",
537 pre: Some((1000, 5, Some(bytecode1))),
538 post: Some((1000, 5, Some(bytecode1))),
539 expected_balance: Delta::Unchanged,
540 expected_nonce: Delta::Unchanged,
541 expected_code: Delta::Unchanged,
542 },
543 TestCase {
544 name: "Balance increase",
545 pre: Some((1000, 5, Some(bytecode1))),
546 post: Some((2000, 5, Some(bytecode1))),
547 expected_balance: Delta::Changed(ChangedType {
548 from: EthBigInt::from(1000),
549 to: EthBigInt::from(2000),
550 }),
551 expected_nonce: Delta::Unchanged,
552 expected_code: Delta::Unchanged,
553 },
554 TestCase {
555 name: "Nonce increment",
556 pre: Some((1000, 5, Some(bytecode1))),
557 post: Some((1000, 6, Some(bytecode1))),
558 expected_balance: Delta::Unchanged,
559 expected_nonce: Delta::Changed(ChangedType {
560 from: EthUint64(5),
561 to: EthUint64(6),
562 }),
563 expected_code: Delta::Unchanged,
564 },
565 TestCase {
566 name: "Bytecode change",
567 pre: Some((1000, 5, Some(bytecode1))),
568 post: Some((1000, 5, Some(bytecode2))),
569 expected_balance: Delta::Unchanged,
570 expected_nonce: Delta::Unchanged,
571 expected_code: Delta::Changed(ChangedType {
572 from: EthBytes(bytecode1.to_vec()),
573 to: EthBytes(bytecode2.to_vec()),
574 }),
575 },
576 TestCase {
577 name: "Balance and Nonce change",
578 pre: Some((1000, 5, Some(bytecode1))),
579 post: Some((2000, 6, Some(bytecode1))),
580 expected_balance: Delta::Changed(ChangedType {
581 from: EthBigInt::from(1000),
582 to: EthBigInt::from(2000),
583 }),
584 expected_nonce: Delta::Changed(ChangedType {
585 from: EthUint64(5),
586 to: EthUint64(6),
587 }),
588 expected_code: Delta::Unchanged,
589 },
590 TestCase {
591 name: "Creation",
592 pre: None,
593 post: Some((5000, 0, Some(bytecode1))),
594 expected_balance: Delta::Added(EthBigInt::from(5000)),
595 expected_nonce: Delta::Added(EthUint64(0)),
596 expected_code: Delta::Added(EthBytes(bytecode1.to_vec())),
597 },
598 TestCase {
599 name: "Deletion",
600 pre: Some((3000, 10, Some(bytecode1))),
601 post: None,
602 expected_balance: Delta::Removed(EthBigInt::from(3000)),
603 expected_nonce: Delta::Removed(EthUint64(10)),
604 expected_code: Delta::Removed(EthBytes(bytecode1.to_vec())),
605 },
606 ];
607
608 for case in cases {
609 let store = Arc::new(MemoryDB::default());
610 let actor_id = 10000u64; let pre_actor = case.pre.and_then(|(bal, nonce, code)| {
613 create_evm_actor_with_bytecode(&store, bal, 0, nonce, code)
614 });
615 let post_actor = case.post.and_then(|(bal, nonce, code)| {
616 create_evm_actor_with_bytecode(&store, bal, 0, nonce, code)
617 });
618
619 let mut pre_state = StateTree::new(&store, StateTreeVersion::V5).unwrap();
620 let mut post_state = StateTree::new(&store, StateTreeVersion::V5).unwrap();
621 let addr = FilecoinAddress::new_id(actor_id);
622
623 if let Some(actor) = pre_actor {
624 pre_state.set_actor(&addr, actor).unwrap();
625 }
626 if let Some(actor) = post_actor {
627 post_state.set_actor(&addr, actor).unwrap();
628 }
629
630 let mut touched_addresses = HashSet::new();
631 touched_addresses.insert(create_masked_id_eth_address(actor_id));
632
633 let state_diff =
634 build_state_diff(store.as_ref(), &pre_state, &post_state, &touched_addresses)
635 .unwrap();
636
637 if case.expected_balance == Delta::Unchanged
638 && case.expected_nonce == Delta::Unchanged
639 && case.expected_code == Delta::Unchanged
640 {
641 assert!(
642 state_diff.0.is_empty(),
643 "Test case '{}' failed: expected empty diff",
644 case.name
645 );
646 } else {
647 let eth_addr = create_masked_id_eth_address(actor_id);
648 let diff = state_diff.0.get(ð_addr).unwrap_or_else(|| {
649 panic!("Test case '{}' failed: missing diff entry", case.name)
650 });
651
652 assert_eq!(
653 diff.balance, case.expected_balance,
654 "Test case '{}' failed: balance mismatch",
655 case.name
656 );
657 assert_eq!(
658 diff.nonce, case.expected_nonce,
659 "Test case '{}' failed: nonce mismatch",
660 case.name
661 );
662 assert_eq!(
663 diff.code, case.expected_code,
664 "Test case '{}' failed: code mismatch",
665 case.name
666 );
667 }
668 }
669 }
670
671 #[test]
672 fn test_build_state_diff_non_evm_actor_no_code() {
673 let actor_id = 4005u64;
675 let pre_actor = create_test_actor(1000, 5);
676 let post_actor = create_test_actor(2000, 6);
677 let trees = TestStateTrees::with_changed_actor(actor_id, pre_actor, post_actor).unwrap();
678
679 let mut touched_addresses = HashSet::new();
680 touched_addresses.insert(create_masked_id_eth_address(actor_id));
681
682 let state_diff = trees.build_diff(&touched_addresses).unwrap();
683
684 let eth_addr = create_masked_id_eth_address(actor_id);
685 let diff = state_diff.0.get(ð_addr).unwrap();
686
687 assert!(!diff.balance.is_unchanged());
689 assert!(!diff.nonce.is_unchanged());
690
691 assert!(diff.code.is_unchanged());
693 }
694
695 #[test]
696 fn test_actor_nonce_non_evm() {
697 let store = MemoryDB::default();
698 let actor = create_test_actor(1000, 42);
699 let nonce = actor.eth_nonce(&store).unwrap();
700 assert_eq!(nonce.0, 42);
701 }
702
703 #[test]
704 fn test_actor_nonce_evm() {
705 let store = Arc::new(MemoryDB::default());
706 let actor = create_evm_actor_with_bytecode(&store, 1000, 0, 7, Some(&[0x60]))
707 .expect("failed to create EVM actor fixture");
708 let nonce = actor.eth_nonce(store.as_ref()).unwrap();
709 assert_eq!(nonce.0, 7);
711 }
712
713 #[test]
714 fn test_actor_bytecode_non_evm() {
715 let store = MemoryDB::default();
716 let actor = create_test_actor(1000, 0);
717 assert!(actor.eth_bytecode(&store).unwrap().is_none());
718 }
719
720 #[test]
721 fn test_actor_bytecode_evm() {
722 let store = Arc::new(MemoryDB::default());
723 let bytecode = &[0x60, 0x80, 0x60, 0x40, 0x52];
724 let actor = create_evm_actor_with_bytecode(&store, 1000, 0, 1, Some(bytecode))
725 .expect("failed to create EVM actor fixture");
726 let result = actor.eth_bytecode(store.as_ref()).unwrap();
727 assert_eq!(result, Some(EthBytes(bytecode.to_vec())));
728 }
729
730 #[test]
731 fn test_actor_bytecode_evm_no_bytecode() {
732 let store = Arc::new(MemoryDB::default());
733 let actor = create_evm_actor_with_bytecode(&store, 1000, 0, 1, None)
734 .expect("failed to create EVM actor fixture");
735 let result = actor.eth_bytecode(store.as_ref()).unwrap();
737 assert!(result.is_none());
738 }
739
740 #[test]
741 fn test_diff_entry_keys_both_empty() {
742 let pre = HashMap::default();
743 let post = HashMap::default();
744 let keys = diff_entry_keys(&pre, &post);
745 assert!(keys.is_empty());
746 }
747
748 #[test]
749 fn test_diff_entry_keys_non_evm_actors() {
750 let store = MemoryDB::default();
751 let pre = create_test_actor(1000, 5);
752 let post = create_test_actor(2000, 6);
753 let pre_entries = extract_evm_storage_entries(&store, Some(&pre));
755 let post_entries = extract_evm_storage_entries(&store, Some(&post));
756 let keys = diff_entry_keys(&pre_entries, &post_entries);
757 assert!(keys.is_empty());
758 }
759
760 #[test]
761 fn test_diff_entry_keys_pre_none_post_non_evm() {
762 let store = MemoryDB::default();
763 let post = create_test_actor(1000, 0);
764 let pre_entries = extract_evm_storage_entries(&store, None);
765 let post_entries = extract_evm_storage_entries(&store, Some(&post));
766 let keys = diff_entry_keys(&pre_entries, &post_entries);
767 assert!(keys.is_empty());
768 }
769}