1use crate::{
7 executive_observer::TracerTrait,
8 internal_contract::{
9 block_hash_slot, epoch_hash_slot, suicide as suicide_impl,
10 InternalRefContext,
11 },
12 machine::Machine,
13 return_if,
14 stack::{CallStackInfo, FrameLocal, RuntimeRes},
15 state::State,
16 substate::Substate,
17};
18use cfx_bytes::Bytes;
19use cfx_parameters::staking::{
20 code_collateral_units, DRIPS_PER_STORAGE_COLLATERAL_UNIT,
21};
22use cfx_types::{
23 cal_contract_address_with_space, Address, AddressSpaceUtil,
24 AddressWithSpace, BigEndianHash, CreateContractAddressType, Space, H256,
25 U256,
26};
27use cfx_vm_types::{
28 self as vm, ActionParams, ActionValue, CallType, Context as ContextTrait,
29 ContractCreateResult, CreateType, Env, Error, MessageCallResult,
30 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: CreateContractAddressType,
268 ) -> cfx_statedb::Result<std::result::Result<ContractCreateResult, TrapKind>>
269 {
270 let caller = AddressWithSpace {
271 address: self.origin.address,
272 space: self.space,
273 };
274
275 let create_type = CreateType::from_address_scheme(&address_scheme);
276 let (address_with_space, code_hash) = cal_contract_address_with_space(
278 address_scheme,
279 &caller,
280 &self.state.nonce(&caller)?,
281 &code,
282 );
283
284 let address = address_with_space.address;
285
286 let conflict_address = if !self.spec.cip_c2_fix {
291 self.space == Space::Native
292 && self.state.is_contract_with_code(&address_with_space)?
293 } else {
294 !self.state.is_eip684_empty(&address_with_space)?
295 };
296
297 if conflict_address {
298 if self.spec.cip645.fix_eip684 {
299 self.state.inc_nonce(&caller)?;
300 }
301 debug!("Contract address conflict!");
302 let err = Error::ConflictAddress(address.clone());
303 return Ok(Ok(ContractCreateResult::Failed(err)));
304 }
305
306 let params = ActionParams {
308 space: self.space,
309 code_address: address.clone(),
310 address: address.clone(),
311 sender: self.origin.address.clone(),
312 original_sender: self.origin.original_sender,
313 storage_owner: self.origin.storage_owner,
314 gas: *gas,
315 gas_price: self.origin.gas_price,
316 value: ActionValue::Transfer(*value),
317 code: Some(Arc::new(code.to_vec())),
318 code_hash,
319 data: None,
320 call_type: CallType::None,
321 create_type,
322 params_type: vm::ParamsType::Embedded,
323 };
324
325 if !self.is_static_or_reentrancy() {
326 let nonce_overflow = self.state.inc_nonce(&caller)?;
327 if nonce_overflow {
328 let err = Error::NonceOverflow(caller.address);
329 return Ok(Ok(ContractCreateResult::Failed(err)));
330 }
331 }
332
333 return Ok(Err(TrapKind::Create(params)));
334 }
335
336 fn call(
337 &mut self, gas: &U256, sender_address: &Address,
338 receive_address: &Address, value: Option<U256>, data: &[u8],
339 code_address: &Address, call_type: CallType,
340 ) -> cfx_statedb::Result<std::result::Result<MessageCallResult, TrapKind>>
341 {
342 trace!(target: "context", "call");
343
344 let code_address_with_space = code_address.with_space(self.space);
345
346 let (code, code_hash) = if let Some(contract) = self
347 .machine
348 .internal_contracts()
349 .contract(&code_address_with_space, self.spec)
350 {
351 (Some(contract.code()), contract.code_hash())
352 } else {
353 self.state
354 .code_with_hash_on_call(&code_address_with_space)?
355 };
356
357 let mut params = ActionParams {
358 space: self.space,
359 sender: *sender_address,
360 address: *receive_address,
361 value: ActionValue::Apparent(self.origin.value),
362 code_address: *code_address,
363 original_sender: self.origin.original_sender,
364 storage_owner: self.origin.storage_owner,
365 gas: *gas,
366 gas_price: self.origin.gas_price,
367 code,
368 code_hash,
369 data: Some(data.to_vec()),
370 call_type,
371 create_type: CreateType::None,
372 params_type: vm::ParamsType::Separate,
373 };
374
375 if let Some(value) = value {
376 params.value = ActionValue::Transfer(value);
377 }
378
379 return Ok(Err(TrapKind::Call(params)));
380 }
381
382 fn extcode(&self, address: &Address) -> vm::Result<Option<Arc<Bytes>>> {
383 let address = address.with_space(self.space);
384 if let Some(contract) = self
385 .machine
386 .internal_contracts()
387 .contract(&address, self.spec)
388 {
389 Ok(Some(contract.code()))
390 } else {
391 Ok(self.state.code(&address)?)
392 }
393 }
394
395 fn extcodehash(&self, address: &Address) -> vm::Result<H256> {
396 let address = address.with_space(self.space);
397
398 if let Some(contract) = self
399 .machine
400 .internal_contracts()
401 .contract(&address, self.spec)
402 {
403 Ok(contract.code_hash())
404 } else {
405 if self.spec.cip645.fix_extcodehash
406 && self.state.is_eip158_empty(&address)?
407 {
408 Ok(H256::zero())
409 } else {
410 Ok(self.state.code_hash(&address)?)
411 }
412 }
413 }
414
415 fn extcodesize(&self, address: &Address) -> vm::Result<usize> {
416 let address = address.with_space(self.space);
417
418 if let Some(contract) = self
419 .machine
420 .internal_contracts()
421 .contract(&address, self.spec)
422 {
423 Ok(contract.code_size())
424 } else {
425 Ok(self.state.code_size(&address)?)
426 }
427 }
428
429 fn log(&mut self, topics: Vec<H256>, data: &[u8]) -> vm::Result<()> {
430 use primitives::log_entry::LogEntry;
431
432 if self.is_static_or_reentrancy() {
433 return Err(vm::Error::MutableCallInStaticContext);
434 }
435
436 self.tracer.log(&self.origin.address, &topics, data);
437
438 let address = self.origin.address.clone();
439 self.substate.logs.push(LogEntry {
440 address,
441 topics,
442 data: data.to_vec(),
443 space: self.space,
444 });
445
446 Ok(())
447 }
448
449 fn refund(&mut self, refund_gas: i64) {
450 self.substate.refund_gas += refund_gas as i128;
451 }
452
453 fn ret(
454 mut self, gas: &U256, data: &ReturnData, apply_state: bool,
455 ) -> vm::Result<U256>
456 where Self: Sized {
457 let caller = self.origin.address.with_space(self.space);
458
459 if self.create_address.is_none() || !apply_state {
460 return Ok(*gas);
461 }
462
463 if self.spec.cip7702 && data.first().cloned() == Some(0xef) {
464 return Err(Error::CreateContractStartingWithEF);
465 }
466
467 self.insert_create_address_to_substate();
468
469 let create_data_gas = self.spec.create_data_gas
470 * match self.space {
471 Space::Native => 1,
472 Space::Ethereum => self.spec.evm_gas_ratio,
473 };
474 let return_cost = U256::from(data.len()) * create_data_gas;
475 if return_cost > *gas || data.len() > self.spec.create_data_limit {
476 return Err(vm::Error::OutOfGas);
477 }
478
479 if self.space == Space::Native {
480 let collateral_units_for_code = code_collateral_units(data.len());
481 let collateral_in_drips = U256::from(collateral_units_for_code)
482 * *DRIPS_PER_STORAGE_COLLATERAL_UNIT;
483 debug!("ret() collateral_for_code={:?}", collateral_in_drips);
484 self.substate.record_storage_occupy(
485 &self.origin.storage_owner,
486 collateral_units_for_code,
487 );
488 }
489
490 let owner = if self.space == Space::Native {
491 self.origin.storage_owner
492 } else {
493 Address::zero()
494 };
495
496 let tx_hash = self.env.transaction_hash;
497 assert_ne!(tx_hash, H256::zero());
498 self.state
499 .init_code(&caller, data.to_vec(), owner, tx_hash)?;
500 Ok(*gas - return_cost)
501 }
502
503 fn suicide(&mut self, refund_address: &Address) -> vm::Result<()> {
504 if self.is_static_or_reentrancy() {
505 return Err(vm::Error::MutableCallInStaticContext);
506 }
507
508 let contract_address_with_space =
509 self.origin.address.with_space(self.space);
510
511 suicide_impl(
512 &contract_address_with_space,
513 &refund_address.with_space(self.space),
514 self.state,
515 &self.spec,
516 &mut self.substate,
517 self.env,
518 self.callstack
519 .creating_contract(&contract_address_with_space),
520 self.tracer,
521 )
522 }
523
524 fn spec(&self) -> &Spec { &self.spec }
525
526 fn env(&self) -> &Env { &self.env }
527
528 fn space(&self) -> Space { self.space }
529
530 fn chain_id(&self) -> u64 { self.env.chain_id[&self.space] as u64 }
531
532 fn depth(&self) -> usize { self.depth }
533
534 fn trace_step(&mut self, interpreter: &dyn vm::InterpreterInfo) {
535 self.tracer.step(interpreter);
536 }
537
538 fn trace_step_end(&mut self, interpreter: &dyn vm::InterpreterInfo) {
539 self.tracer.step_end(interpreter);
540 }
541
542 fn opcode_trace_enabled(&self) -> bool {
543 let mut enabled = false;
544 self.tracer.do_trace_opcode(&mut enabled);
545 enabled
546 }
547
548 fn is_static(&self) -> bool { self.static_flag }
549
550 fn is_static_or_reentrancy(&self) -> bool {
551 self.static_flag || self.callstack.in_reentrancy(self.spec)
552 }
553
554 fn blockhash_source(&self) -> vm::BlockHashSource {
555 let from_state = match self.space {
556 Space::Native => self.env.number >= self.spec.cip133_b,
557 Space::Ethereum => self.env.epoch_height >= self.spec.cip133_e,
558 };
559 if from_state {
560 BlockHashSource::State
561 } else {
562 BlockHashSource::Env
563 }
564 }
565
566 fn is_warm_account(&self, account: Address) -> bool {
567 let address_with_space = account.with_space(self.space);
568 let maybe_builtin = &account[..19] == &[0u8; 19];
569 if maybe_builtin
570 && self
571 .machine
572 .builtin(
573 &address_with_space,
574 self.env.number,
575 self.env.epoch_height,
576 )
577 .is_some()
578 {
579 return true;
580 }
581
582 let maybe_internal = self.space == Space::Native
583 && &account[..2] == b"\x08\x88"
584 && &account[2..19] == &[0u8; 17];
585 if maybe_internal
586 && self
587 .machine
588 .internal_contracts()
589 .contract(&address_with_space, &self.spec)
590 .is_some()
591 {
592 return true;
593 }
594
595 if address_with_space == self.env.author.with_native_space() {
596 return true;
597 }
598
599 self.state.is_warm_account(&address_with_space)
600 }
601
602 fn is_warm_storage_entry(&self, key: &H256) -> vm::Result<bool> {
603 let receiver = AddressWithSpace {
604 address: self.origin.address,
605 space: self.space,
606 };
607 Ok(self.state.is_warm_storage_entry(&receiver, key)?)
608 }
609}
610
611impl<'a> Context<'a> {
612 pub fn internal_ref(&mut self) -> InternalRefContext<'_> {
613 InternalRefContext {
614 env: self.env,
615 spec: self.spec,
616 callstack: self.callstack,
617 state: self.state,
618 substate: &mut self.substate,
619 static_flag: self.static_flag,
620 depth: self.depth,
621 tracer: self.tracer,
622 }
623 }
624
625 pub fn insert_create_address_to_substate(&mut self) {
626 if let Some(create_address) = self.create_address {
627 self.substate
628 .record_contract_create(create_address.with_space(self.space));
629 }
630 }
631}
632
633#[cfg(test)]
636mod tests {
637 use super::{FrameLocal, OriginInfo};
638 use crate::{
639 machine::Machine,
640 stack::{CallStackInfo, OwnedRuntimeRes},
641 state::{get_state_for_genesis_write, State},
642 substate::Substate,
643 tests::MOCK_TX_HASH,
644 };
645 use cfx_parameters::consensus::TRANSACTION_DEFAULT_EPOCH_BOUND;
646 use cfx_types::{
647 address_util::AddressUtil, Address, AddressSpaceUtil, Space, H256, U256,
648 };
649 use cfx_vm_types::{Context as ContextTrait, Env, Spec};
650 use std::{collections::BTreeMap, str::FromStr};
651
652 fn get_test_origin() -> OriginInfo {
653 let mut sender = Address::zero();
654 sender.set_user_account_type_bits();
655 OriginInfo {
656 address: sender,
657 original_sender: sender,
658 storage_owner: Address::zero(),
659 gas_price: U256::zero(),
660 value: U256::zero(),
661 }
662 }
663
664 fn get_test_env() -> Env {
665 Env {
666 chain_id: BTreeMap::from([
667 (Space::Native, 0),
668 (Space::Ethereum, 0),
669 ]),
670 number: 100,
671 author: Address::from_low_u64_be(0),
672 timestamp: 0,
673 difficulty: 0.into(),
674 last_hash: H256::zero(),
675 accumulated_gas_used: 0.into(),
676 gas_limit: 0.into(),
677 epoch_height: 0,
678 pos_view: None,
679 finalized_epoch: None,
680 transaction_epoch_bound: TRANSACTION_DEFAULT_EPOCH_BOUND,
681 base_gas_price: Default::default(),
682 burnt_gas_price: Default::default(),
683 transaction_hash: MOCK_TX_HASH,
684 #[cfg(feature = "align_evm")]
685 blob_gas_fee: 0.into(),
686 }
687 }
688
689 #[allow(unused)]
692 struct TestSetup {
693 state: State,
694 machine: Machine,
695 spec: Spec,
696 substate: Substate,
697 env: Env,
698 callstack: CallStackInfo,
699 }
700
701 impl TestSetup {
702 fn new() -> Self {
703 let state = get_state_for_genesis_write();
704 let machine = Machine::new_with_builtin(
705 Default::default(),
706 Default::default(),
707 );
708 let env = get_test_env();
709 let spec = machine.spec_for_test(env.number);
710 let callstack = CallStackInfo::new();
711
712 let mut setup = Self {
713 state,
715 machine,
716 spec,
717 substate: Substate::new(),
718 env: env.clone(),
719 callstack,
720 };
721 setup
722 .state
723 .init_code(
724 &Address::zero().with_native_space(),
725 vec![],
726 Address::zero(),
727 env.transaction_hash,
728 )
729 .ok();
730
731 setup
732 }
733 }
734
735 #[test]
736 fn can_be_created() {
737 let mut setup = TestSetup::new();
738 let state = &mut setup.state;
739 let origin = get_test_origin();
740
741 let mut lctx = FrameLocal::new(
742 Space::Native,
743 &setup.env,
744 &setup.machine,
745 &setup.spec,
746 0, origin,
748 setup.substate,
749 None,
750 false, );
752 let mut owned_res = OwnedRuntimeRes::from(state);
753 let mut resources = owned_res.as_res();
754 let ctx = lctx.make_vm_context(&mut resources);
755
756 assert_eq!(ctx.env().number, 100);
757 }
758
759 #[test]
760 fn can_return_block_hash_no_env() {
761 let mut setup = TestSetup::new();
762 let state = &mut setup.state;
763 let origin = get_test_origin();
764
765 let mut lctx = FrameLocal::new(
766 Space::Native,
767 &setup.env,
768 &setup.machine,
769 &setup.spec,
770 0, origin,
772 setup.substate,
773 None,
774 false, );
776 let mut owned_res = OwnedRuntimeRes::from(state);
777 let mut resources = owned_res.as_res();
778 let mut ctx = lctx.make_vm_context(&mut resources);
779
780 let hash = ctx.blockhash(
781 &"0000000000000000000000000000000000000000000000000000000000120000"
782 .parse::<U256>()
783 .unwrap(),
784 ).unwrap();
785
786 assert_eq!(hash, H256::zero());
787 }
788
789 #[test]
875 fn can_log() {
876 let log_data = vec![120u8, 110u8];
877 let log_topics = vec![H256::from_str(
878 "af0fa234a6af46afa23faf23bcbc1c1cb4bcb7bcbe7e7e7ee3ee2edddddddddd",
879 )
880 .unwrap()];
881
882 let mut setup = TestSetup::new();
883 let state = &mut setup.state;
884 let origin = get_test_origin();
885
886 {
887 let mut lctx = FrameLocal::new(
888 Space::Native,
889 &setup.env,
890 &setup.machine,
891 &setup.spec,
892 0, origin,
894 setup.substate,
895 None,
896 false, );
898
899 {
900 let mut owned_res = OwnedRuntimeRes::from(state);
901 let mut resources = owned_res.as_res();
902
903 let mut ctx = lctx.make_vm_context(&mut resources);
904 ctx.log(log_topics, &log_data).unwrap();
905 }
906
907 assert_eq!(lctx.substate.logs.len(), 1);
908 }
909 }
910
911 #[test]
912 fn can_suicide() {
913 let mut refund_account = Address::zero();
914 refund_account.set_user_account_type_bits();
915
916 let mut setup = TestSetup::new();
917 setup.spec.cip151 = false;
918 let state = &mut setup.state;
919 let mut origin = get_test_origin();
920
921 let mut contract_address = Address::zero();
922 contract_address.set_contract_type_bits();
923 origin.address = contract_address;
924 let contract_address_w_space = contract_address.with_native_space();
925 state
926 .new_contract_with_code(&contract_address_w_space, U256::zero())
927 .expect(&concat!(file!(), ":", line!(), ":", column!()));
928 state
929 .init_code(
930 &contract_address_w_space,
931 "".into(),
934 contract_address,
935 setup.env.transaction_hash,
936 )
937 .expect(&concat!(file!(), ":", line!(), ":", column!()));
938
939 {
940 let mut lctx = FrameLocal::new(
941 Space::Native,
942 &setup.env,
943 &setup.machine,
944 &setup.spec,
945 0, origin,
947 setup.substate,
948 None,
949 false, );
951 let mut owned_res = OwnedRuntimeRes::from(state);
952 let mut resources = owned_res.as_res();
953 let mut ctx = lctx.make_vm_context(&mut resources);
954 ctx.suicide(&refund_account).unwrap();
955 assert_eq!(lctx.substate.suicides.len(), 1);
956 }
957 }
958
959 }