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