From 5507b9357bb5a3f26622c11a7466d337ec10bbfb Mon Sep 17 00:00:00 2001 From: ZenGround0 <5515260+ZenGround0@users.noreply.github.com> Date: Thu, 28 Apr 2022 11:26:03 -0400 Subject: [PATCH] Integration test vm: runtime, testing facilities, first integration test (#267) * Most Integration test runtime methods implemented * Invocation tracing * Invocation expecation type for test matching * VM constructor with singleton actors and good state * First complete integration test Co-authored-by: zenground0 --- actors/init/src/types.rs | 1 + actors/power/src/lib.rs | 1 - actors/power/src/types.rs | 2 +- actors/runtime/Cargo.toml | 2 +- actors/runtime/src/test_utils.rs | 16 +- test_vm/src/lib.rs | 452 ++++++++++++++++++++++---- test_vm/tests/power_scenario_tests.rs | 94 ++++++ test_vm/tests/test_vm_test.rs | 16 +- 8 files changed, 509 insertions(+), 75 deletions(-) create mode 100644 test_vm/tests/power_scenario_tests.rs diff --git a/actors/init/src/types.rs b/actors/init/src/types.rs index 3d13234c4..2464d9e49 100644 --- a/actors/init/src/types.rs +++ b/actors/init/src/types.rs @@ -29,3 +29,4 @@ pub struct ExecReturn { } impl Cbor for ExecReturn {} +impl Cbor for ExecParams {} diff --git a/actors/power/src/lib.rs b/actors/power/src/lib.rs index 71796d75a..f34769db8 100644 --- a/actors/power/src/lib.rs +++ b/actors/power/src/lib.rs @@ -102,7 +102,6 @@ impl Actor { })?; let miner_actor_code_cid = rt.get_code_cid_for_type(Type::Miner); - let ext::init::ExecReturn { id_address, robust_address } = rt .send( *INIT_ACTOR_ADDR, diff --git a/actors/power/src/types.rs b/actors/power/src/types.rs index 4362af00d..99c0e8890 100644 --- a/actors/power/src/types.rs +++ b/actors/power/src/types.rs @@ -23,7 +23,7 @@ pub const CRON_QUEUE_HAMT_BITWIDTH: u32 = 6; pub const CRON_QUEUE_AMT_BITWIDTH: u32 = 6; pub const PROOF_VALIDATION_BATCH_AMT_BITWIDTH: u32 = 4; -#[derive(Serialize_tuple, Deserialize_tuple)] +#[derive(Serialize_tuple, Deserialize_tuple, Clone)] pub struct CreateMinerParams { pub owner: Address, pub worker: Address, diff --git a/actors/runtime/Cargo.toml b/actors/runtime/Cargo.toml index 8c989cd82..1813dbd1b 100644 --- a/actors/runtime/Cargo.toml +++ b/actors/runtime/Cargo.toml @@ -4,7 +4,7 @@ description = "System actors for the Filecoin protocol" version = "8.0.0-alpha.1" license = "MIT OR Apache-2.0" authors = ["ChainSafe Systems ", "Protocol Labs", "Filecoin Core Devs"] -edition = "2018" +edition = "2021" repository = "https://github.com/filecoin-project/builtin-actors" [dependencies] diff --git a/actors/runtime/src/test_utils.rs b/actors/runtime/src/test_utils.rs index ad308a3d8..b38b9cc6f 100644 --- a/actors/runtime/src/test_utils.rs +++ b/actors/runtime/src/test_utils.rs @@ -65,6 +65,21 @@ lazy_static! { map.insert(*VERIFREG_ACTOR_CODE_ID, Type::VerifiedRegistry); map }; + pub static ref ACTOR_CODES: BTreeMap = [ + (Type::System, *SYSTEM_ACTOR_CODE_ID), + (Type::Init, *INIT_ACTOR_CODE_ID), + (Type::Cron, *CRON_ACTOR_CODE_ID), + (Type::Account, *ACCOUNT_ACTOR_CODE_ID), + (Type::Power, *POWER_ACTOR_CODE_ID), + (Type::Miner, *MINER_ACTOR_CODE_ID), + (Type::Market, *MARKET_ACTOR_CODE_ID), + (Type::PaymentChannel, *PAYCH_ACTOR_CODE_ID), + (Type::Multisig, *MULTISIG_ACTOR_CODE_ID), + (Type::Reward, *REWARD_ACTOR_CODE_ID), + (Type::VerifiedRegistry, *VERIFREG_ACTOR_CODE_ID), + ] + .into_iter() + .collect(); pub static ref CALLER_TYPES_SIGNABLE: Vec = vec![*ACCOUNT_ACTOR_CODE_ID, *MULTISIG_ACTOR_CODE_ID]; pub static ref NON_SINGLETON_CODES: BTreeMap = { @@ -1159,7 +1174,6 @@ impl RuntimePolicy for MockRuntime { } pub fn blake2b_256(data: &[u8]) -> [u8; 32] { - use std::convert::TryInto; blake2b_simd::Params::new() .hash_length(32) .to_state() diff --git a/test_vm/src/lib.rs b/test_vm/src/lib.rs index b1eff0c27..21cb70c83 100644 --- a/test_vm/src/lib.rs +++ b/test_vm/src/lib.rs @@ -1,22 +1,26 @@ use cid::multihash::Code; use cid::Cid; -use fil_actor_account::Actor as AccountActor; -use fil_actor_cron::Actor as CronActor; -use fil_actor_init::{Actor as InitActor, State as InitState}; -use fil_actor_market::Actor as MarketActor; +use fil_actor_account::{Actor as AccountActor, State as AccountState}; +use fil_actor_cron::{Actor as CronActor, Entry as CronEntry, State as CronState}; +use fil_actor_init::{Actor as InitActor, ExecReturn, State as InitState}; +use fil_actor_market::{Actor as MarketActor, Method as MarketMethod, State as MarketState}; use fil_actor_miner::Actor as MinerActor; use fil_actor_multisig::Actor as MultisigActor; use fil_actor_paych::Actor as PaychActor; -use fil_actor_power::Actor as PowerActor; -use fil_actor_reward::Actor as RewardActor; +use fil_actor_power::{Actor as PowerActor, Method as MethodPower, State as PowerState}; +use fil_actor_reward::{Actor as RewardActor, State as RewardState}; use fil_actor_system::{Actor as SystemActor, State as SystemState}; -use fil_actor_verifreg::Actor as VerifregActor; +use fil_actor_verifreg::{Actor as VerifregActor, State as VerifRegState}; use fil_actors_runtime::cbor::serialize; use fil_actors_runtime::runtime::{ ActorCode, MessageInfo, Policy, Primitives, Runtime, RuntimePolicy, Verifier, }; use fil_actors_runtime::test_utils::*; -use fil_actors_runtime::{ActorError, INIT_ACTOR_ADDR, SYSTEM_ACTOR_ADDR}; +use fil_actors_runtime::{ + ActorError, BURNT_FUNDS_ACTOR_ADDR, FIRST_NON_SINGLETON_ADDR, INIT_ACTOR_ADDR, + REWARD_ACTOR_ADDR, STORAGE_MARKET_ACTOR_ADDR, STORAGE_POWER_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, + VERIFIED_REGISTRY_ACTOR_ADDR, +}; use fvm_ipld_blockstore::MemoryBlockstore; use fvm_ipld_encoding::tuple::*; use fvm_ipld_encoding::{Cbor, CborStore, RawBytes}; @@ -34,12 +38,13 @@ use fvm_shared::piece::PieceInfo; use fvm_shared::randomness::Randomness; use fvm_shared::sector::{ AggregateSealVerifyProofAndInfos, RegisteredSealProof, ReplicaUpdateInfo, SealVerifyInfo, - WindowPoStVerifyInfo, + StoragePower, WindowPoStVerifyInfo, }; use fvm_shared::version::NetworkVersion; use fvm_shared::{ActorID, MethodNum, METHOD_CONSTRUCTOR, METHOD_SEND}; use num_traits::Signed; -use std::cell::RefCell; +use serde::ser; +use std::cell::{RefCell, RefMut}; use std::collections::HashMap; use std::error::Error; use std::fmt; @@ -51,9 +56,16 @@ pub struct VM<'bs> { actors_dirty: RefCell, actors_cache: RefCell>, empty_obj_cid: Cid, - // invocationStack + network_version: NetworkVersion, + curr_epoch: ChainEpoch, + invocations: RefCell>, } +pub const VERIFREG_ROOT_KEY: &[u8] = &[200; fvm_shared::address::BLS_PUB_LEN]; +// Account actor seeding funds created by new_with_singletons +pub const FAUCET_ROOT_KEY: &[u8] = &[153; fvm_shared::address::BLS_PUB_LEN]; +pub const TEST_FAUCET_ADDR: Address = Address::new_id(FIRST_NON_SINGLETON_ADDR + 2); +pub const FIRST_TEST_USER_ADDR: ActorID = FIRST_NON_SINGLETON_ADDR + 3; // accounts for verifreg root signer and msig impl<'bs> VM<'bs> { pub fn new(store: &'bs MemoryBlockstore) -> VM<'bs> { let mut actors = Hamt::<&'bs MemoryBlockstore, Actor, BytesKey, Sha256>::new(store); @@ -64,26 +76,146 @@ impl<'bs> VM<'bs> { actors_dirty: RefCell::new(false), actors_cache: RefCell::new(HashMap::new()), empty_obj_cid: empty, + network_version: NetworkVersion::V16, + curr_epoch: ChainEpoch::zero(), + invocations: RefCell::new(vec![]), } } pub fn new_with_singletons(store: &'bs MemoryBlockstore) -> VM<'bs> { - // craft init state directly + // funding + let fil = TokenAmount::from(1_000_000_000i32) + .checked_mul(&TokenAmount::from(1_000_000_000i32)) + .unwrap(); + let reward_total = TokenAmount::from(1_100_000_000i32).checked_mul(&fil).unwrap(); + let faucet_total = TokenAmount::from(1_000_000_000u32).checked_mul(&fil).unwrap(); + let v = VM::new(store); + + // system + let sys_st = SystemState::new(store).unwrap(); + let sys_head = v.put_store(&sys_st); + let sys_value = faucet_total.clone(); // delegate faucet funds to system so we can construct faucet by sending to bls addr + v.set_actor(*SYSTEM_ACTOR_ADDR, actor(*SYSTEM_ACTOR_CODE_ID, sys_head, 0, sys_value)); + + // init let init_st = InitState::new(store, "integration-test".to_string()).unwrap(); - let init_head = store.put_cbor(&init_st, Code::Blake2b256).unwrap(); + let init_head = v.put_store(&init_st); v.set_actor( *INIT_ACTOR_ADDR, - actor(*INIT_ACTOR_CODE_ID, init_head, 0, BigInt::from(200_000_000)), + actor(*INIT_ACTOR_CODE_ID, init_head, 0, TokenAmount::zero()), ); - let sys_st = SystemState::new(store).unwrap(); - let sys_head = store.put_cbor(&sys_st, Code::Blake2b256).unwrap(); - v.set_actor(*SYSTEM_ACTOR_ADDR, actor(*SYSTEM_ACTOR_CODE_ID, sys_head, 0, BigInt::zero())); + + // reward + + let reward_head = v.put_store(&RewardState::new(StoragePower::zero())); + v.set_actor(*REWARD_ACTOR_ADDR, actor(*REWARD_ACTOR_CODE_ID, reward_head, 0, reward_total)); + + // cron + let builtin_entries = vec![ + CronEntry { + receiver: *STORAGE_POWER_ACTOR_ADDR, + method_num: MethodPower::OnEpochTickEnd as u64, + }, + CronEntry { + receiver: *STORAGE_MARKET_ACTOR_ADDR, + method_num: MarketMethod::CronTick as u64, + }, + ]; + let cron_head = v.put_store(&CronState { entries: builtin_entries }); + v.set_actor( + *STORAGE_MARKET_ACTOR_ADDR, + actor(*MARKET_ACTOR_CODE_ID, cron_head, 0, TokenAmount::zero()), + ); + + // power + let power_head = v.put_store(&PowerState::new(&v.store).unwrap()); + v.set_actor( + *STORAGE_POWER_ACTOR_ADDR, + actor(*POWER_ACTOR_CODE_ID, power_head, 0, TokenAmount::zero()), + ); + + // market + let market_head = v.put_store(&MarketState::new(&v.store).unwrap()); + v.set_actor( + *STORAGE_MARKET_ACTOR_ADDR, + actor(*MARKET_ACTOR_CODE_ID, market_head, 0, TokenAmount::zero()), + ); + + // verifreg + // initialize verifreg root signer + v.apply_message( + *INIT_ACTOR_ADDR, + Address::new_bls(VERIFREG_ROOT_KEY).unwrap(), + TokenAmount::zero(), + METHOD_SEND, + RawBytes::default(), + ) + .unwrap(); + let verifreg_root_signer = + v.normalize_address(&Address::new_bls(VERIFREG_ROOT_KEY).unwrap()).unwrap(); + // verifreg root msig + let msig_ctor_params = serialize( + &fil_actor_multisig::ConstructorParams { + signers: vec![verifreg_root_signer], + num_approvals_threshold: 1, + unlock_duration: 0, + start_epoch: 0, + }, + "multisig ctor params", + ) + .unwrap(); + let msig_ctor_ret: ExecReturn = v + .apply_message( + *SYSTEM_ACTOR_ADDR, + *INIT_ACTOR_ADDR, + BigInt::zero(), + fil_actor_init::Method::Exec as u64, + fil_actor_init::ExecParams { + code_cid: *MULTISIG_ACTOR_CODE_ID, + constructor_params: msig_ctor_params, + }, + ) + .unwrap() + .ret + .deserialize() + .unwrap(); + let root_msig_addr = msig_ctor_ret.id_address; + // verifreg + let verifreg_head = v.put_store(&VerifRegState::new(&v.store, root_msig_addr).unwrap()); + v.set_actor( + *VERIFIED_REGISTRY_ACTOR_ADDR, + actor(*VERIFREG_ACTOR_CODE_ID, verifreg_head, 0, TokenAmount::zero()), + ); + + // burnt funds + let burnt_funds_head = v.put_store(&AccountState { address: *BURNT_FUNDS_ACTOR_ADDR }); + v.set_actor( + *BURNT_FUNDS_ACTOR_ADDR, + actor(*ACCOUNT_ACTOR_CODE_ID, burnt_funds_head, 0, TokenAmount::zero()), + ); + + // create a faucet with 1 billion FIL for setting up test accounts + v.apply_message( + *SYSTEM_ACTOR_ADDR, + Address::new_bls(FAUCET_ROOT_KEY).unwrap(), + faucet_total, + METHOD_SEND, + RawBytes::default(), + ) + .unwrap(); + v.checkpoint(); - // TODO: actually add remaining singletons for testing v } + pub fn put_store(&self, obj: &S) -> Cid + where + S: ser::Serialize, + { + self.store.put_cbor(obj, Code::Blake2b256).unwrap() + } + pub fn get_actor(&self, addr: Address) -> Option { // check for inclusion in cache of changed actors if let Some(act) = self.actors_cache.borrow().get(&addr) { @@ -152,17 +284,17 @@ impl<'bs> VM<'bs> { ) -> Result { let from_id = self.normalize_address(&from).unwrap(); let mut a = self.get_actor(from_id).unwrap(); - a.call_seq_num += 1; - let call_seq_num = a.call_seq_num; + let call_seq = a.call_seq_num; + a.call_seq_num = call_seq + 1; self.set_actor(from_id, a); let prior_root = self.checkpoint(); // make top level context with internal context let top = TopCtx { - _originator_stable_addr: to, - _originator_call_seq: call_seq_num, - _new_actor_addr_count: 0, + originator_stable_addr: from, + _originator_call_seq: call_seq, + new_actor_addr_count: RefCell::new(0), _circ_supply: BigInt::zero(), }; let msg = InternalMessage { @@ -176,11 +308,17 @@ impl<'bs> VM<'bs> { v: self, top, msg, - allow_side_effects: false, - _caller_validated: false, + allow_side_effects: true, + caller_validated: false, policy: &Policy::default(), + subinvocations: RefCell::new(vec![]), }; let res = new_ctx.invoke(); + let invoc = new_ctx.gather_trace(res.clone()); + RefMut::map(self.invocations.borrow_mut(), |invocs| { + invocs.push(invoc); + invocs + }); match res { Err(ae) => { self.rollback(prior_root); @@ -192,12 +330,16 @@ impl<'bs> VM<'bs> { } } } + + pub fn take_invocations(&self) -> Vec { + self.invocations.take() + } } #[derive(Clone)] pub struct TopCtx { - _originator_stable_addr: Address, + originator_stable_addr: Address, _originator_call_seq: u64, - _new_actor_addr_count: u64, + new_actor_addr_count: RefCell, _circ_supply: BigInt, } @@ -222,13 +364,16 @@ impl MessageInfo for InternalMessage { } } +pub const TEST_VM_RAND_STRING: &str = "i_am_random_____i_am_random_____"; + pub struct InvocationCtx<'invocation, 'bs> { v: &'invocation VM<'bs>, top: TopCtx, msg: InternalMessage, allow_side_effects: bool, - _caller_validated: bool, + caller_validated: bool, policy: &'invocation Policy, + subinvocations: RefCell>, } impl<'invocation, 'bs> InvocationCtx<'invocation, 'bs> { @@ -267,17 +412,36 @@ impl<'invocation, 'bs> InvocationCtx<'invocation, 'bs> { v: self.v, top: self.top.clone(), msg: new_actor_msg, - allow_side_effects: false, - _caller_validated: false, + allow_side_effects: true, + caller_validated: false, policy: self.policy, + subinvocations: RefCell::new(vec![]), }; new_ctx.create_actor(*ACCOUNT_ACTOR_CODE_ID, target_id).unwrap(); - _ = new_ctx.invoke(); + let res = new_ctx.invoke(); + let invoc = new_ctx.gather_trace(res); + RefMut::map(self.subinvocations.borrow_mut(), |subinvocs| { + subinvocs.push(invoc); + subinvocs + }); } Ok((self.v.get_actor(target_id_addr).unwrap(), target_id_addr)) } + fn gather_trace(&mut self, invoke_result: Result) -> InvocationTrace { + let (ret, code) = match invoke_result { + Ok(rb) => (Some(rb), None), + Err(ae) => (None, Some(ae.exit_code())), + }; + InvocationTrace { + msg: self.msg.clone(), + code, + ret, + subinvocations: self.subinvocations.take(), + } + } + fn invoke(&mut self) -> Result { let prior_root = self.v.checkpoint(); @@ -366,7 +530,7 @@ impl<'invocation, 'bs> Runtime for InvocationCtx<'invocation, } fn network_version(&self) -> NetworkVersion { - panic!("TODO implement me") + self.v.network_version } fn message(&self) -> &dyn MessageInfo { @@ -374,37 +538,64 @@ impl<'invocation, 'bs> Runtime for InvocationCtx<'invocation, } fn curr_epoch(&self) -> ChainEpoch { - panic!("TODO implement me") + self.v.curr_epoch } fn validate_immediate_caller_accept_any(&mut self) -> Result<(), ActorError> { - panic!("TODO implement me") + if self.caller_validated { + Err(ActorError::unchecked( + ExitCode::SYS_ASSERTION_FAILED, + "caller double validated".to_string(), + )) + } else { + self.caller_validated = true; + Ok(()) + } } fn validate_immediate_caller_is<'a, I>(&mut self, addresses: I) -> Result<(), ActorError> where I: IntoIterator, { + if self.caller_validated { + return Err(ActorError::unchecked( + ExitCode::USR_ASSERTION_FAILED, + "caller double validated".to_string(), + )); + } for addr in addresses { if *addr == self.msg.from { return Ok(()); } } Err(ActorError::unchecked( - ExitCode::SYS_ASSERTION_FAILED, + ExitCode::USR_FORBIDDEN, "immediate caller address forbidden".to_string(), )) } - fn validate_immediate_caller_type<'a, I>(&mut self, _types: I) -> Result<(), ActorError> + fn validate_immediate_caller_type<'a, I>(&mut self, types: I) -> Result<(), ActorError> where I: IntoIterator, { - panic!("TODO implement me") + if self.caller_validated { + return Err(ActorError::unchecked( + ExitCode::SYS_ASSERTION_FAILED, + "caller double validated".to_string(), + )); + } + let to_match = ACTOR_TYPES.get(&self.v.get_actor(self.msg.from).unwrap().code).unwrap(); + if types.into_iter().any(|t| *t == *to_match) { + return Ok(()); + } + Err(ActorError::unchecked( + ExitCode::SYS_ASSERTION_FAILED, + "immediate caller actor type forbidden".to_string(), + )) } fn current_balance(&self) -> TokenAmount { - panic!("TODO implement me") + self.v.get_actor(self.msg.to).unwrap().balance } fn resolve_address(&self, addr: &Address) -> Option
{ @@ -438,11 +629,21 @@ impl<'invocation, 'bs> Runtime for InvocationCtx<'invocation, v: self.v, top: self.top.clone(), msg: new_actor_msg, - allow_side_effects: false, - _caller_validated: false, + allow_side_effects: true, + caller_validated: false, policy: self.policy, + subinvocations: RefCell::new(vec![]), }; - new_ctx.invoke() + println!("starting send invoc [{}:{}]", to, method); + let res = new_ctx.invoke(); + println!("finished send invoc [{}:{}]", to, method); + + let invoc = new_ctx.gather_trace(res.clone()); + RefMut::map(self.subinvocations.borrow_mut(), |subinvocs| { + subinvocs.push(invoc); + subinvocs + }); + res } fn get_randomness_from_tickets( @@ -451,7 +652,7 @@ impl<'invocation, 'bs> Runtime for InvocationCtx<'invocation, _rand_epoch: ChainEpoch, _entropy: &[u8], ) -> Result { - panic!("TODO implement me") + Ok(Randomness(TEST_VM_RAND_STRING.as_bytes().to_vec())) } fn get_randomness_from_beacon( @@ -460,7 +661,7 @@ impl<'invocation, 'bs> Runtime for InvocationCtx<'invocation, _rand_epoch: ChainEpoch, _entropy: &[u8], ) -> Result { - panic!("TODO implement me") + Ok(Randomness(TEST_VM_RAND_STRING.as_bytes().to_vec())) } fn create(&mut self, obj: &C) -> Result<(), ActorError> { @@ -486,35 +687,51 @@ impl<'invocation, 'bs> Runtime for InvocationCtx<'invocation, } fn state(&self) -> Result { - panic!("TODO implement me") + Ok(self.v.get_state::(self.msg.to).unwrap()) } - fn transaction(&mut self, _f: F) -> Result + fn transaction(&mut self, f: F) -> Result where C: Cbor, F: FnOnce(&mut C, &mut Self) -> Result, { - panic!("TODO implement me") + let mut st = self.state::().unwrap(); + self.allow_side_effects = false; + let result = f(&mut st, self); + self.allow_side_effects = true; + let ret = result?; + let mut act = self.v.get_actor(self.msg.to).unwrap(); + act.head = self.v.store.put_cbor(&st, Code::Blake2b256).unwrap(); + self.v.set_actor(self.msg.to, act); + Ok(ret) } fn new_actor_address(&mut self) -> Result { - panic!("TODO implement me") + let osa_bytes = self.top.originator_stable_addr.to_bytes(); + let mut seq_num_bytes = self.top.originator_stable_addr.to_bytes(); + let cnt = self.top.new_actor_addr_count.take(); + self.top.new_actor_addr_count.replace(cnt + 1); + let mut cnt_bytes = serialize(&cnt, "count failed").unwrap().to_vec(); + let mut out = osa_bytes; + out.append(&mut seq_num_bytes); + out.append(&mut cnt_bytes); + Ok(Address::new_actor(out.as_slice())) } fn delete_actor(&mut self, _beneficiary: &Address) -> Result<(), ActorError> { panic!("TODO implement me") } - fn resolve_builtin_actor_type(&self, _code_id: &Cid) -> Option { - panic!("TODO implement me") + fn resolve_builtin_actor_type(&self, code_id: &Cid) -> Option { + ACTOR_TYPES.get(code_id).cloned() } - fn get_code_cid_for_type(&self, _typ: Type) -> Cid { - panic!("TODO implement me") + fn get_code_cid_for_type(&self, typ: Type) -> Cid { + ACTOR_CODES.get(&typ).cloned().unwrap() } fn total_fil_circ_supply(&self) -> TokenAmount { - panic!("TODO implement me") + self.top._circ_supply.clone() } fn charge_gas(&mut self, _name: &'static str, _compute: i64) {} @@ -531,11 +748,15 @@ impl Primitives for InvocationCtx<'_, '_> { _signer: &Address, _plaintext: &[u8], ) -> Result<(), anyhow::Error> { - panic!("TODO implement me") + Ok(()) } fn hash_blake2b(&self, _data: &[u8]) -> [u8; 32] { - panic!("TODO implement me") + // TODO: actual blake 2b + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ] } fn compute_unsealed_sector_cid( @@ -549,11 +770,11 @@ impl Primitives for InvocationCtx<'_, '_> { impl Verifier for InvocationCtx<'_, '_> { fn verify_seal(&self, _vi: &SealVerifyInfo) -> Result<(), anyhow::Error> { - panic!("TODO implement me") + Ok(()) } fn verify_post(&self, _verify_info: &WindowPoStVerifyInfo) -> Result<(), anyhow::Error> { - panic!("TODO implement me") + Ok(()) } fn verify_consensus_fault( @@ -562,22 +783,22 @@ impl Verifier for InvocationCtx<'_, '_> { _h2: &[u8], _extra: &[u8], ) -> Result, anyhow::Error> { - panic!("TODO implement me") + Ok(None) } - fn batch_verify_seals(&self, _batch: &[SealVerifyInfo]) -> anyhow::Result> { - panic!("TODO implement me") + fn batch_verify_seals(&self, batch: &[SealVerifyInfo]) -> anyhow::Result> { + Ok(vec![true; batch.len()]) // everyone wins } fn verify_aggregate_seals( &self, _aggregate: &AggregateSealVerifyProofAndInfos, ) -> Result<(), anyhow::Error> { - panic!("TODO implement me") + Ok(()) } fn verify_replica_update(&self, _replica: &ReplicaUpdateInfo) -> Result<(), anyhow::Error> { - panic!("TODO implement me") + Ok(()) } } @@ -606,6 +827,115 @@ pub fn actor(code: Cid, head: Cid, seq: u64, bal: TokenAmount) -> Actor { Actor { code, head, call_seq_num: seq, balance: bal } } +pub struct InvocationTrace { + pub msg: InternalMessage, + pub code: Option, + pub ret: Option, + pub subinvocations: Vec, +} + +pub struct ExpectInvocation { + pub to: Address, // required + pub method: MethodNum, // required + pub code: Option, + pub from: Option
, + pub params: Option, + pub ret: Option, + pub subinvocs: Option>, +} + +impl ExpectInvocation { + // testing method that panics on no match + pub fn matches(&self, invoc: &InvocationTrace) { + let id = format!("[{}:{}]", invoc.msg.to, invoc.msg.method); + self.quick_match(invoc, String::new()); + if let Some(c) = self.code { + assert_ne!( + None, + invoc.code, + "{} unexpected code: expected:{}was:{}", + id, + c, + ExitCode::OK + ); + assert_eq!( + c, + invoc.code.unwrap(), + "{} unexpected code expected:{}was:{}", + id, + c, + invoc.code.unwrap() + ); + } + if let Some(f) = self.from { + assert_eq!( + f, invoc.msg.from, + "{} unexpected from addr: expected:{}was:{} ", + id, f, invoc.msg.from + ); + } + if let Some(p) = &self.params { + assert_eq!( + p, &invoc.msg.params, + "{} unexpected params: expected:{:x?}was:{:x?}", + id, p, invoc.msg.params + ); + } + if let Some(r) = &self.ret { + assert_ne!(None, invoc.ret, "{} unexpected ret: expected:{:x?}was:None", id, r); + let ret = &invoc.ret.clone().unwrap(); + assert_eq!(r, ret, "{} unexpected ret: expected:{:x?}was:{:x?}", id, r, ret); + } + if let Some(expect_subinvocs) = &self.subinvocs { + let subinvocs = &invoc.subinvocations; + + let panic_str = format!( + "unexpected subinvocs:\n expected: \n[\n{}]\n was:\n[\n{}]\n", + self.fmt_expect_invocs(expect_subinvocs), + self.fmt_invocs(subinvocs) + ); + assert!(subinvocs.len() == expect_subinvocs.len(), "{}", panic_str); + + for (i, invoc) in subinvocs.iter().enumerate() { + let expect_invoc = expect_subinvocs.get(i).unwrap(); + // only try to match if required fields match + expect_invoc.quick_match(invoc, panic_str.clone()); + expect_invoc.matches(invoc); + } + } + } + + pub fn fmt_invocs(&self, invocs: &[InvocationTrace]) -> String { + invocs + .iter() + .enumerate() + .map(|(i, invoc)| format!("{}: [{}:{}],\n", i, invoc.msg.to, invoc.msg.method)) + .collect() + } + + pub fn fmt_expect_invocs(&self, invocs: &[ExpectInvocation]) -> String { + invocs + .iter() + .enumerate() + .map(|(i, invoc)| format!("{}: [{}:{}],\n", i, invoc.to, invoc.method)) + .collect() + } + + pub fn quick_match(&self, invoc: &InvocationTrace, extra_msg: String) { + let id = format!("[{}:{}]", invoc.msg.to, invoc.msg.method); + assert_eq!( + self.to, invoc.msg.to, + "{} unexpected to addr: expected:{} was:{} \n{}", + id, self.to, invoc.msg.to, extra_msg + ); + assert_eq!( + self.method, invoc.msg.method, + "{} unexpected method: expected:{}was:{} \n{}", + id, self.method, invoc.msg.from, extra_msg + ); + } +} + #[derive(Debug)] pub struct TestVMError { msg: String, diff --git a/test_vm/tests/power_scenario_tests.rs b/test_vm/tests/power_scenario_tests.rs new file mode 100644 index 000000000..fe8966d32 --- /dev/null +++ b/test_vm/tests/power_scenario_tests.rs @@ -0,0 +1,94 @@ +use fil_actor_init::Method as InitMethod; +use fil_actor_miner::{Method as MinerMethod, MinerConstructorParams}; +use fil_actor_power::{CreateMinerParams, Method as PowerMethod}; +use fil_actors_runtime::cbor::serialize; + +use fil_actors_runtime::{INIT_ACTOR_ADDR, STORAGE_POWER_ACTOR_ADDR}; +use fvm_ipld_blockstore::MemoryBlockstore; +use fvm_ipld_encoding::{BytesDe, RawBytes}; +use fvm_shared::address::Address; +use fvm_shared::econ::TokenAmount; +use fvm_shared::sector::RegisteredPoStProof; +use fvm_shared::METHOD_SEND; +use test_vm::{ExpectInvocation, FIRST_TEST_USER_ADDR, TEST_FAUCET_ADDR, VM}; + +#[test] +fn create_miner() { + let store = MemoryBlockstore::new(); + let v = VM::new_with_singletons(&store); + + let owner = Address::new_bls(&[1; fvm_shared::address::BLS_PUB_LEN]).unwrap(); + v.apply_message( + TEST_FAUCET_ADDR, + owner, + TokenAmount::from(10_000u32), + METHOD_SEND, + RawBytes::default(), + ) + .unwrap(); + let multiaddrs = vec![BytesDe("multiaddr".as_bytes().to_vec())]; + let peer_id = "miner".as_bytes().to_vec(); + let params = CreateMinerParams { + owner, + worker: owner, + window_post_proof_type: RegisteredPoStProof::StackedDRGWindow32GiBV1, + peer: peer_id.clone(), + multiaddrs: multiaddrs.clone(), + }; + + let res = v + .apply_message( + owner, + *STORAGE_POWER_ACTOR_ADDR, + TokenAmount::from(1000u32), + PowerMethod::CreateMiner as u64, + params.clone(), + ) + .unwrap(); + + let expect = ExpectInvocation { + // send to power actor + to: *STORAGE_POWER_ACTOR_ADDR, + method: PowerMethod::CreateMiner as u64, + params: Some(serialize(¶ms, "power create miner params").unwrap()), + code: None, + from: None, + ret: Some(res.ret), + subinvocs: Some(vec![ + // request init actor construct miner + ExpectInvocation { + to: *INIT_ACTOR_ADDR, + method: InitMethod::Exec as u64, + params: None, + code: None, + from: None, + ret: None, + subinvocs: Some(vec![ExpectInvocation { + // init then calls miner constructor + to: Address::new_id(FIRST_TEST_USER_ADDR + 1), + method: MinerMethod::Constructor as u64, + params: Some( + serialize( + &MinerConstructorParams { + owner, + worker: owner, + window_post_proof_type: + RegisteredPoStProof::StackedDRGWindow32GiBV1, + peer_id, + control_addresses: vec![], + multi_addresses: multiaddrs, + }, + "miner constructor params", + ) + .unwrap(), + ), + code: None, + from: None, + ret: None, + subinvocs: None, + }]), + }, + ]), + }; + expect.matches(v.take_invocations().last().unwrap()) +} diff --git a/test_vm/tests/test_vm_test.rs b/test_vm/tests/test_vm_test.rs index 8849b8d70..ed945dd1c 100644 --- a/test_vm/tests/test_vm_test.rs +++ b/test_vm/tests/test_vm_test.rs @@ -1,16 +1,12 @@ use fil_actor_account::State as AccountState; -use fil_actors_runtime::FIRST_NON_SINGLETON_ADDR; -use fil_actors_runtime::{ - test_utils::{make_builtin, ACCOUNT_ACTOR_CODE_ID, PAYCH_ACTOR_CODE_ID}, - INIT_ACTOR_ADDR, -}; +use fil_actors_runtime::test_utils::{make_builtin, ACCOUNT_ACTOR_CODE_ID, PAYCH_ACTOR_CODE_ID}; use fvm_ipld_blockstore::MemoryBlockstore; use fvm_ipld_encoding::RawBytes; use fvm_shared::address::Address; use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; use fvm_shared::METHOD_SEND; -use test_vm::{actor, VM}; +use test_vm::{actor, FIRST_TEST_USER_ADDR, TEST_FAUCET_ADDR, VM}; #[test] fn state_control() { @@ -60,21 +56,21 @@ fn test_sent() { // send to uninitialized account actor let addr1 = Address::new_bls(&[1; fvm_shared::address::BLS_PUB_LEN]).unwrap(); v.apply_message( - *INIT_ACTOR_ADDR, + TEST_FAUCET_ADDR, addr1, TokenAmount::from(42u8), METHOD_SEND, RawBytes::default(), ) .unwrap(); - let expect_id_addr1 = Address::new_id(FIRST_NON_SINGLETON_ADDR); + let expect_id_addr1 = Address::new_id(FIRST_TEST_USER_ADDR); assert_account_actor(0, TokenAmount::from(42u8), addr1, &v, expect_id_addr1); // send from this account actor to another uninit account actor let addr2 = Address::new_bls(&[2; fvm_shared::address::BLS_PUB_LEN]).unwrap(); v.apply_message(addr1, addr2, TokenAmount::from(41u8), METHOD_SEND, RawBytes::default()) .unwrap(); - let expect_id_addr2 = Address::new_id(FIRST_NON_SINGLETON_ADDR + 1); + let expect_id_addr2 = Address::new_id(FIRST_TEST_USER_ADDR + 1); assert_account_actor(0, TokenAmount::from(41u8), addr2, &v, expect_id_addr2); // send between two initialized account actors @@ -100,7 +96,7 @@ fn test_sent() { let mres = v .apply_message( addr1, - Address::new_id(99), + Address::new_id(88), TokenAmount::from(1u8), METHOD_SEND, RawBytes::default(),