1use crate::{
7 executive::contract_address,
8 executive_observer::TracerTrait,
9 internal_contract::{
10 block_hash_slot, epoch_hash_slot, suicide as suicide_impl,
11 InternalRefContext,
12 },
13 machine::Machine,
14 return_if,
15 stack::{CallStackInfo, FrameLocal, RuntimeRes},
16 state::State,
17 substate::Substate,
18};
19use cfx_bytes::Bytes;
20use cfx_parameters::staking::{
21 code_collateral_units, DRIPS_PER_STORAGE_COLLATERAL_UNIT,
22};
23use cfx_types::{
24 Address, AddressSpaceUtil, AddressWithSpace, BigEndianHash, Space, H256,
25 U256,
26};
27use cfx_vm_types::{
28 self as vm, ActionParams, ActionValue, CallType, Context as ContextTrait,
29 ContractCreateResult, CreateContractAddress, CreateType, Env, Error,
30 MessageCallResult, ReturnData, Spec, TrapKind,
31};
32use std::sync::Arc;
33use vm::BlockHashSource;
34
35#[derive(Debug)]
37pub struct OriginInfo {
38 address: Address,
39 original_sender: Address,
41 storage_owner: Address,
44 gas_price: U256,
45 value: U256,
46}
47
48impl OriginInfo {
49 pub fn from(params: &ActionParams) -> Self {
51 OriginInfo {
52 address: params.address,
53 original_sender: params.original_sender,
54 storage_owner: params.storage_owner,
55 gas_price: params.gas_price,
56 value: match params.value {
57 ActionValue::Transfer(val) | ActionValue::Apparent(val) => val,
58 },
59 }
60 }
61
62 pub fn recipient(&self) -> &Address { &self.address }
63}
64
65pub struct Context<'a> {
66 space: Space,
67 env: &'a Env,
68 depth: usize,
69 create_address: &'a Option<Address>,
70 origin: &'a OriginInfo,
71 substate: &'a mut Substate,
72 machine: &'a Machine,
73 spec: &'a Spec,
74 static_flag: bool,
75
76 state: &'a mut State,
77 callstack: &'a mut CallStackInfo,
78 tracer: &'a mut dyn TracerTrait,
79}
80
81impl<'a> Context<'a> {
82 pub fn new<'b, 'c>(
83 frame_local: &'a mut FrameLocal<'b>,
84 runtime_resources: &'a mut RuntimeRes<'c>,
85 ) -> Self {
86 let space = frame_local.space;
87 let env = &frame_local.env;
88 let depth = frame_local.depth;
89 let create_address = &frame_local.create_address;
90 let origin = &frame_local.origin;
91 let substate = &mut frame_local.substate;
92 let machine = frame_local.machine;
93 let spec = frame_local.spec;
94 let static_flag = frame_local.static_flag;
95
96 let state = &mut *runtime_resources.state;
97 let callstack = &mut *runtime_resources.callstack;
98 let tracer = &mut *runtime_resources.tracer;
99 Context {
100 space,
101 env,
102 depth,
103 create_address,
104 origin,
105 substate,
106 machine,
107 spec,
108 static_flag,
109 state,
110 callstack,
111 tracer,
112 }
113 }
114
115 fn blockhash_from_env(&self, number: &U256) -> H256 {
116 if self.space == Space::Ethereum && self.spec.cip98 {
117 return if U256::from(self.env().epoch_height) == number + 1 {
118 self.env().last_hash.clone()
119 } else {
120 H256::default()
121 };
122 }
123
124 if U256::from(self.env().number) == number + 1 {
127 self.env().last_hash.clone()
128 } else {
129 H256::default()
130 }
131 }
132
133 fn blockhash_from_state(&self, number: &U256) -> vm::Result<H256> {
134 return_if!(number > &U256::from(u64::MAX));
135
136 let number = number.as_u64();
137
138 let state_res = match self.space {
139 Space::Native => {
140 return_if!(number < self.spec.cip133_b);
141 return_if!(number > self.env.number);
142 return_if!(number
143 .checked_add(65536)
144 .map_or(false, |n| n <= self.env.number));
145 self.state.get_system_storage(&block_hash_slot(number))?
146 }
147 Space::Ethereum => {
148 return_if!(number < self.spec.cip133_e);
149 return_if!(number > self.env.epoch_height);
150 return_if!(number
151 .checked_add(65536)
152 .map_or(false, |n| n <= self.env.epoch_height));
153 if self.spec.align_evm {
154 return_if!(number >= self.env.epoch_height);
155 return_if!(number + 256 < self.env.epoch_height);
156 }
157 self.state.get_system_storage(&epoch_hash_slot(number))?
158 }
159 };
160
161 Ok(BigEndianHash::from_uint(&state_res))
162 }
163}
164
165impl<'a> ContextTrait for Context<'a> {
166 fn storage_at(&self, key: &[u8]) -> vm::Result<U256> {
167 let receiver = AddressWithSpace {
168 address: self.origin.address,
169 space: self.space,
170 };
171 self.state.storage_at(&receiver, key).map_err(Into::into)
172 }
173
174 fn origin_storage_at(&self, key: &[u8]) -> vm::Result<Option<U256>> {
175 let receiver = AddressWithSpace {
176 address: self.origin.address,
177 space: self.space,
178 };
179 self.state
180 .origin_storage_at(&receiver, key)
181 .map_err(Into::into)
182 }
183
184 fn set_storage(&mut self, key: Vec<u8>, value: U256) -> vm::Result<()> {
185 let receiver = AddressWithSpace {
186 address: self.origin.address,
187 space: self.space,
188 };
189 if self.is_static_or_reentrancy() {
190 Err(vm::Error::MutableCallInStaticContext)
191 } else {
192 self.state
193 .set_storage(
194 &receiver,
195 key,
196 value,
197 self.origin.storage_owner,
198 &mut self.substate,
199 )
200 .map_err(Into::into)
201 }
202 }
203
204 fn transient_storage_at(&self, key: &Vec<u8>) -> vm::Result<U256> {
205 let receiver = AddressWithSpace {
206 address: self.origin.address,
207 space: self.space,
208 };
209 self.state
210 .transient_storage_at(&receiver, key)
211 .map_err(Into::into)
212 }
213
214 fn transient_set_storage(
215 &mut self, key: Vec<u8>, value: U256,
216 ) -> vm::Result<()> {
217 let receiver = AddressWithSpace {
218 address: self.origin.address,
219 space: self.space,
220 };
221 if self.is_static_or_reentrancy() {
222 Err(vm::Error::MutableCallInStaticContext)
223 } else {
224 self.state
225 .transient_set_storage(&receiver, key, value)
226 .map_err(Into::into)
227 }
228 }
229
230 fn exists(&self, address: &Address) -> vm::Result<bool> {
231 let address = AddressWithSpace {
232 address: *address,
233 space: self.space,
234 };
235 self.state.exists(&address).map_err(Into::into)
236 }
237
238 fn exists_and_not_null(&self, address: &Address) -> vm::Result<bool> {
239 let address = AddressWithSpace {
240 address: *address,
241 space: self.space,
242 };
243 self.state.exists_and_not_null(&address).map_err(Into::into)
244 }
245
246 fn origin_balance(&self) -> vm::Result<U256> {
247 self.balance(&self.origin.address).map_err(Into::into)
248 }
249
250 fn balance(&self, address: &Address) -> vm::Result<U256> {
251 let address = AddressWithSpace {
252 address: *address,
253 space: self.space,
254 };
255 self.state.balance(&address).map_err(Into::into)
256 }
257
258 fn blockhash(&mut self, number: &U256) -> vm::Result<H256> {
259 match self.blockhash_source() {
260 BlockHashSource::Env => Ok(self.blockhash_from_env(number)),
261 BlockHashSource::State => self.blockhash_from_state(number),
262 }
263 }
264
265 fn create(
266 &mut self, gas: &U256, value: &U256, code: &[u8],
267 address_scheme: CreateContractAddress,
268 ) -> cfx_statedb::Result<
269 ::std::result::Result<ContractCreateResult, TrapKind>,
270 > {
271 let caller = AddressWithSpace {
272 address: self.origin.address,
273 space: self.space,
274 };
275
276 let create_type = CreateType::from_address_scheme(&address_scheme);
277 let (address_with_space, code_hash) = self::contract_address(
279 address_scheme,
280 self.env.number.into(),
281 &caller,
282 &self.state.nonce(&caller)?,
283 &code,
284 );
285
286 let address = address_with_space.address;
287
288 let conflict_address = if !self.spec.cip_c2_fix {
293 self.space == Space::Native
294 && self.state.is_contract_with_code(&address_with_space)?
295 } else {
296 !self.state.is_eip684_empty(&address_with_space)?
297 };
298
299 if conflict_address {
300 if self.spec.cip645.fix_eip684 {
301 self.state.inc_nonce(&caller)?;
302 }
303 debug!("Contract address conflict!");
304 let err = Error::ConflictAddress(address.clone());
305 return Ok(Ok(ContractCreateResult::Failed(err)));
306 }
307
308 let params = ActionParams {
310 space: self.space,
311 code_address: address.clone(),
312 address: address.clone(),
313 sender: self.origin.address.clone(),
314 original_sender: self.origin.original_sender,
315 storage_owner: self.origin.storage_owner,
316 gas: *gas,
317 gas_price: self.origin.gas_price,
318 value: ActionValue::Transfer(*value),
319 code: Some(Arc::new(code.to_vec())),
320 code_hash,
321 data: None,
322 call_type: CallType::None,
323 create_type,
324 params_type: vm::ParamsType::Embedded,
325 };
326
327 if !self.is_static_or_reentrancy() {
328 let nonce_overflow = self.state.inc_nonce(&caller)?;
329 if nonce_overflow {
330 let err = Error::NonceOverflow(caller.address);
331 return Ok(Ok(ContractCreateResult::Failed(err)));
332 }
333 }
334
335 return Ok(Err(TrapKind::Create(params)));
336 }
337
338 fn call(
339 &mut self, gas: &U256, sender_address: &Address,
340 receive_address: &Address, value: Option<U256>, data: &[u8],
341 code_address: &Address, call_type: CallType,
342 ) -> cfx_statedb::Result<::std::result::Result<MessageCallResult, TrapKind>>
343 {
344 trace!(target: "context", "call");
345
346 let code_address_with_space = code_address.with_space(self.space);
347
348 let (code, code_hash) = if let Some(contract) = self
349 .machine
350 .internal_contracts()
351 .contract(&code_address_with_space, self.spec)
352 {
353 (Some(contract.code()), contract.code_hash())
354 } else {
355 self.state
356 .code_with_hash_on_call(&code_address_with_space)?
357 };
358
359 let mut params = ActionParams {
360 space: self.space,
361 sender: *sender_address,
362 address: *receive_address,
363 value: ActionValue::Apparent(self.origin.value),
364 code_address: *code_address,
365 original_sender: self.origin.original_sender,
366 storage_owner: self.origin.storage_owner,
367 gas: *gas,
368 gas_price: self.origin.gas_price,
369 code,
370 code_hash,
371 data: Some(data.to_vec()),
372 call_type,
373 create_type: CreateType::None,
374 params_type: vm::ParamsType::Separate,
375 };
376
377 if let Some(value) = value {
378 params.value = ActionValue::Transfer(value);
379 }
380
381 return Ok(Err(TrapKind::Call(params)));
382 }
383
384 fn extcode(&self, address: &Address) -> vm::Result<Option<Arc<Bytes>>> {
385 let address = address.with_space(self.space);
386 if let Some(contract) = self
387 .machine
388 .internal_contracts()
389 .contract(&address, self.spec)
390 {
391 Ok(Some(contract.code()))
392 } else {
393 Ok(self.state.code(&address)?)
394 }
395 }
396
397 fn extcodehash(&self, address: &Address) -> vm::Result<H256> {
398 let address = address.with_space(self.space);
399
400 if let Some(contract) = self
401 .machine
402 .internal_contracts()
403 .contract(&address, self.spec)
404 {
405 Ok(contract.code_hash())
406 } else {
407 if self.spec.cip645.fix_extcodehash
408 && self.state.is_eip158_empty(&address)?
409 {
410 Ok(H256::zero())
411 } else {
412 Ok(self.state.code_hash(&address)?)
413 }
414 }
415 }
416
417 fn extcodesize(&self, address: &Address) -> vm::Result<usize> {
418 let address = address.with_space(self.space);
419
420 if let Some(contract) = self
421 .machine
422 .internal_contracts()
423 .contract(&address, self.spec)
424 {
425 Ok(contract.code_size())
426 } else {
427 Ok(self.state.code_size(&address)?)
428 }
429 }
430
431 fn log(&mut self, topics: Vec<H256>, data: &[u8]) -> vm::Result<()> {
432 use primitives::log_entry::LogEntry;
433
434 if self.is_static_or_reentrancy() {
435 return Err(vm::Error::MutableCallInStaticContext);
436 }
437
438 self.tracer.log(&self.origin.address, &topics, data);
439
440 let address = self.origin.address.clone();
441 self.substate.logs.push(LogEntry {
442 address,
443 topics,
444 data: data.to_vec(),
445 space: self.space,
446 });
447
448 Ok(())
449 }
450
451 fn refund(&mut self, refund_gas: i64) {
452 self.substate.refund_gas += refund_gas as i128;
453 }
454
455 fn ret(
456 mut self, gas: &U256, data: &ReturnData, apply_state: bool,
457 ) -> vm::Result<U256>
458 where Self: Sized {
459 let caller = self.origin.address.with_space(self.space);
460
461 if self.create_address.is_none() || !apply_state {
462 return Ok(*gas);
463 }
464
465 if self.spec.cip7702 && data.first().cloned() == Some(0xef) {
466 return Err(Error::CreateContractStartingWithEF);
467 }
468
469 self.insert_create_address_to_substate();
470
471 let create_data_gas = self.spec.create_data_gas
472 * match self.space {
473 Space::Native => 1,
474 Space::Ethereum => self.spec.evm_gas_ratio,
475 };
476 let return_cost = U256::from(data.len()) * create_data_gas;
477 if return_cost > *gas || data.len() > self.spec.create_data_limit {
478 return Err(vm::Error::OutOfGas);
479 }
480
481 if self.space == Space::Native {
482 let collateral_units_for_code = code_collateral_units(data.len());
483 let collateral_in_drips = U256::from(collateral_units_for_code)
484 * *DRIPS_PER_STORAGE_COLLATERAL_UNIT;
485 debug!("ret() collateral_for_code={:?}", collateral_in_drips);
486 self.substate.record_storage_occupy(
487 &self.origin.storage_owner,
488 collateral_units_for_code,
489 );
490 }
491
492 let owner = if self.space == Space::Native {
493 self.origin.storage_owner
494 } else {
495 Address::zero()
496 };
497
498 let tx_hash = self.env.transaction_hash;
499 assert_ne!(tx_hash, H256::zero());
500 self.state
501 .init_code(&caller, data.to_vec(), owner, tx_hash)?;
502 Ok(*gas - return_cost)
503 }
504
505 fn suicide(&mut self, refund_address: &Address) -> vm::Result<()> {
506 if self.is_static_or_reentrancy() {
507 return Err(vm::Error::MutableCallInStaticContext);
508 }
509
510 let contract_address_with_space =
511 self.origin.address.with_space(self.space);
512
513 suicide_impl(
514 &contract_address_with_space,
515 &refund_address.with_space(self.space),
516 self.state,
517 &self.spec,
518 &mut self.substate,
519 self.env,
520 self.callstack
521 .creating_contract(&contract_address_with_space),
522 self.tracer,
523 )
524 }
525
526 fn spec(&self) -> &Spec { &self.spec }
527
528 fn env(&self) -> &Env { &self.env }
529
530 fn space(&self) -> Space { self.space }
531
532 fn chain_id(&self) -> u64 { self.env.chain_id[&self.space] as u64 }
533
534 fn depth(&self) -> usize { self.depth }
535
536 fn trace_step(&mut self, interpreter: &dyn vm::InterpreterInfo) {
537 self.tracer.step(interpreter);
538 }
539
540 fn trace_step_end(&mut self, interpreter: &dyn vm::InterpreterInfo) {
541 self.tracer.step_end(interpreter);
542 }
543
544 fn opcode_trace_enabled(&self) -> bool {
545 let mut enabled = false;
546 self.tracer.do_trace_opcode(&mut enabled);
547 enabled
548 }
549
550 fn is_static(&self) -> bool { self.static_flag }
551
552 fn is_static_or_reentrancy(&self) -> bool {
553 self.static_flag || self.callstack.in_reentrancy(self.spec)
554 }
555
556 fn blockhash_source(&self) -> vm::BlockHashSource {
557 let from_state = match self.space {
558 Space::Native => self.env.number >= self.spec.cip133_b,
559 Space::Ethereum => self.env.epoch_height >= self.spec.cip133_e,
560 };
561 if from_state {
562 BlockHashSource::State
563 } else {
564 BlockHashSource::Env
565 }
566 }
567
568 fn is_warm_account(&self, account: Address) -> bool {
569 let address_with_space = account.with_space(self.space);
570 let maybe_builtin = &account[..19] == &[0u8; 19];
571 if maybe_builtin
572 && self
573 .machine
574 .builtin(&address_with_space, self.env.number)
575 .is_some()
576 {
577 return true;
578 }
579
580 let maybe_internal = self.space == Space::Native
581 && &account[..2] == b"\x08\x88"
582 && &account[2..19] == &[0u8; 17];
583 if maybe_internal
584 && self
585 .machine
586 .internal_contracts()
587 .contract(&address_with_space, &self.spec)
588 .is_some()
589 {
590 return true;
591 }
592
593 if address_with_space == self.env.author.with_native_space() {
594 return true;
595 }
596
597 self.state.is_warm_account(&address_with_space)
598 }
599
600 fn is_warm_storage_entry(&self, key: &H256) -> vm::Result<bool> {
601 let receiver = AddressWithSpace {
602 address: self.origin.address,
603 space: self.space,
604 };
605 Ok(self.state.is_warm_storage_entry(&receiver, key)?)
606 }
607}
608
609impl<'a> Context<'a> {
610 pub fn internal_ref(&mut self) -> InternalRefContext<'_> {
611 InternalRefContext {
612 env: self.env,
613 spec: self.spec,
614 callstack: self.callstack,
615 state: self.state,
616 substate: &mut self.substate,
617 static_flag: self.static_flag,
618 depth: self.depth,
619 tracer: self.tracer,
620 }
621 }
622
623 pub fn insert_create_address_to_substate(&mut self) {
624 if let Some(create_address) = self.create_address {
625 self.substate
626 .record_contract_create(create_address.with_space(self.space));
627 }
628 }
629}
630
631#[cfg(test)]
634mod tests {
635 use super::{FrameLocal, OriginInfo};
636 use crate::{
637 machine::Machine,
638 stack::{CallStackInfo, OwnedRuntimeRes},
639 state::{get_state_for_genesis_write, State},
640 substate::Substate,
641 tests::MOCK_TX_HASH,
642 };
643 use cfx_parameters::consensus::TRANSACTION_DEFAULT_EPOCH_BOUND;
644 use cfx_types::{
645 address_util::AddressUtil, Address, AddressSpaceUtil, Space, H256, U256,
646 };
647 use cfx_vm_types::{Context as ContextTrait, Env, Spec};
648 use std::{collections::BTreeMap, str::FromStr};
649
650 fn get_test_origin() -> OriginInfo {
651 let mut sender = Address::zero();
652 sender.set_user_account_type_bits();
653 OriginInfo {
654 address: sender,
655 original_sender: sender,
656 storage_owner: Address::zero(),
657 gas_price: U256::zero(),
658 value: U256::zero(),
659 }
660 }
661
662 fn get_test_env() -> Env {
663 Env {
664 chain_id: BTreeMap::from([
665 (Space::Native, 0),
666 (Space::Ethereum, 0),
667 ]),
668 number: 100,
669 author: Address::from_low_u64_be(0),
670 timestamp: 0,
671 difficulty: 0.into(),
672 last_hash: H256::zero(),
673 accumulated_gas_used: 0.into(),
674 gas_limit: 0.into(),
675 epoch_height: 0,
676 pos_view: None,
677 finalized_epoch: None,
678 transaction_epoch_bound: TRANSACTION_DEFAULT_EPOCH_BOUND,
679 base_gas_price: Default::default(),
680 burnt_gas_price: Default::default(),
681 transaction_hash: MOCK_TX_HASH,
682 #[cfg(feature = "align_evm")]
683 blob_gas_fee: 0.into(),
684 }
685 }
686
687 #[allow(unused)]
690 struct TestSetup {
691 state: State,
692 machine: Machine,
693 spec: Spec,
694 substate: Substate,
695 env: Env,
696 callstack: CallStackInfo,
697 }
698
699 impl TestSetup {
700 fn new() -> Self {
701 let state = get_state_for_genesis_write();
702 let machine = Machine::new_with_builtin(
703 Default::default(),
704 Default::default(),
705 );
706 let env = get_test_env();
707 let spec = machine.spec_for_test(env.number);
708 let callstack = CallStackInfo::new();
709
710 let mut setup = Self {
711 state,
713 machine,
714 spec,
715 substate: Substate::new(),
716 env: env.clone(),
717 callstack,
718 };
719 setup
720 .state
721 .init_code(
722 &Address::zero().with_native_space(),
723 vec![],
724 Address::zero(),
725 env.transaction_hash,
726 )
727 .ok();
728
729 setup
730 }
731 }
732
733 #[test]
734 fn can_be_created() {
735 let mut setup = TestSetup::new();
736 let state = &mut setup.state;
737 let origin = get_test_origin();
738
739 let mut lctx = FrameLocal::new(
740 Space::Native,
741 &setup.env,
742 &setup.machine,
743 &setup.spec,
744 0, origin,
746 setup.substate,
747 None,
748 false, );
750 let mut owned_res = OwnedRuntimeRes::from(state);
751 let mut resources = owned_res.as_res();
752 let ctx = lctx.make_vm_context(&mut resources);
753
754 assert_eq!(ctx.env().number, 100);
755 }
756
757 #[test]
758 fn can_return_block_hash_no_env() {
759 let mut setup = TestSetup::new();
760 let state = &mut setup.state;
761 let origin = get_test_origin();
762
763 let mut lctx = FrameLocal::new(
764 Space::Native,
765 &setup.env,
766 &setup.machine,
767 &setup.spec,
768 0, origin,
770 setup.substate,
771 None,
772 false, );
774 let mut owned_res = OwnedRuntimeRes::from(state);
775 let mut resources = owned_res.as_res();
776 let mut ctx = lctx.make_vm_context(&mut resources);
777
778 let hash = ctx.blockhash(
779 &"0000000000000000000000000000000000000000000000000000000000120000"
780 .parse::<U256>()
781 .unwrap(),
782 ).unwrap();
783
784 assert_eq!(hash, H256::zero());
785 }
786
787 #[test]
873 fn can_log() {
874 let log_data = vec![120u8, 110u8];
875 let log_topics = vec![H256::from_str(
876 "af0fa234a6af46afa23faf23bcbc1c1cb4bcb7bcbe7e7e7ee3ee2edddddddddd",
877 )
878 .unwrap()];
879
880 let mut setup = TestSetup::new();
881 let state = &mut setup.state;
882 let origin = get_test_origin();
883
884 {
885 let mut lctx = FrameLocal::new(
886 Space::Native,
887 &setup.env,
888 &setup.machine,
889 &setup.spec,
890 0, origin,
892 setup.substate,
893 None,
894 false, );
896
897 {
898 let mut owned_res = OwnedRuntimeRes::from(state);
899 let mut resources = owned_res.as_res();
900
901 let mut ctx = lctx.make_vm_context(&mut resources);
902 ctx.log(log_topics, &log_data).unwrap();
903 }
904
905 assert_eq!(lctx.substate.logs.len(), 1);
906 }
907 }
908
909 #[test]
910 fn can_suicide() {
911 let mut refund_account = Address::zero();
912 refund_account.set_user_account_type_bits();
913
914 let mut setup = TestSetup::new();
915 setup.spec.cip151 = false;
916 let state = &mut setup.state;
917 let mut origin = get_test_origin();
918
919 let mut contract_address = Address::zero();
920 contract_address.set_contract_type_bits();
921 origin.address = contract_address;
922 let contract_address_w_space = contract_address.with_native_space();
923 state
924 .new_contract_with_code(&contract_address_w_space, U256::zero())
925 .expect(&concat!(file!(), ":", line!(), ":", column!()));
926 state
927 .init_code(
928 &contract_address_w_space,
929 "".into(),
932 contract_address,
933 setup.env.transaction_hash,
934 )
935 .expect(&concat!(file!(), ":", line!(), ":", column!()));
936
937 {
938 let mut lctx = FrameLocal::new(
939 Space::Native,
940 &setup.env,
941 &setup.machine,
942 &setup.spec,
943 0, origin,
945 setup.substate,
946 None,
947 false, );
949 let mut owned_res = OwnedRuntimeRes::from(state);
950 let mut resources = owned_res.as_res();
951 let mut ctx = lctx.make_vm_context(&mut resources);
952 ctx.suicide(&refund_account).unwrap();
953 assert_eq!(lctx.substate.suicides.len(), 1);
954 }
955 }
956
957 }