From 74ae6836a490dc18f3dfd8cca289edfd0eb73771 Mon Sep 17 00:00:00 2001 From: Alex North <445306+anorth@users.noreply.github.com> Date: Mon, 17 Jul 2023 10:10:46 +1200 Subject: [PATCH] Implement actor events from the verified registry --- actors/verifreg/src/events.rs | 105 +++++++ actors/verifreg/src/lib.rs | 85 ++++-- actors/verifreg/tests/harness/mod.rs | 192 ++++++++++-- actors/verifreg/tests/verifreg_actor_test.rs | 292 +++++++++++-------- runtime/src/test_utils.rs | 4 +- runtime/src/util/events.rs | 128 ++++++++ runtime/src/util/mod.rs | 2 + 7 files changed, 620 insertions(+), 188 deletions(-) create mode 100644 actors/verifreg/src/events.rs create mode 100644 runtime/src/util/events.rs diff --git a/actors/verifreg/src/events.rs b/actors/verifreg/src/events.rs new file mode 100644 index 0000000000..b1d3cd8fd2 --- /dev/null +++ b/actors/verifreg/src/events.rs @@ -0,0 +1,105 @@ +use crate::{Allocation, AllocationID, Claim, ClaimID, DataCap}; +use fil_actors_runtime::runtime::Runtime; +use fil_actors_runtime::{ActorError, EventBuilder}; +use fvm_shared::ActorID; + +// A namespace for helpers that build and emit verified registry events. +// REVIEW: would this be better as a simple module with pub fns, called as emit::verifier_balance()? +pub struct Emit {} + +impl Emit { + /// Indicates a new value for a verifier's datacap balance. + /// Note that receiving this event does not necessarily mean the balance has changed. + /// The value is in datacap whole units (not TokenAmount). + pub fn verifier_balance( + rt: &impl Runtime, + verifier: ActorID, + new_balance: &DataCap, + ) -> Result<(), ActorError> { + rt.emit_event( + &EventBuilder::new() + .label("verifier-balance") + .value_indexed("verifier", &verifier)? + .value("balance", new_balance)? + .build(), + ) + } + + /// Indicates a new allocation has been made. + pub fn allocation( + rt: &impl Runtime, + id: AllocationID, + alloc: &Allocation, + ) -> Result<(), ActorError> { + rt.emit_event(&EventBuilder::new().label("allocation").with_allocation(id, alloc)?.build()) + } + + /// Indicates an expired allocation has been removed. + pub fn allocation_removed( + rt: &impl Runtime, + id: AllocationID, + alloc: &Allocation, + ) -> Result<(), ActorError> { + rt.emit_event( + &EventBuilder::new().label("allocation-removed").with_allocation(id, alloc)?.build(), + ) + } + + /// Indicates an allocation has been claimed. + pub fn claim(rt: &impl Runtime, id: ClaimID, claim: &Claim) -> Result<(), ActorError> { + rt.emit_event(&EventBuilder::new().label("claim").with_claim(id, claim)?.build()) + } + + /// Indicates an existing claim has been updated (e.g. with a longer term). + pub fn claim_updated(rt: &impl Runtime, id: ClaimID, claim: &Claim) -> Result<(), ActorError> { + rt.emit_event(&EventBuilder::new().label("claim-updated").with_claim(id, claim)?.build()) + } + + /// Indicates an expired claim has been removed. + pub fn claim_removed(rt: &impl Runtime, id: ClaimID, claim: &Claim) -> Result<(), ActorError> { + rt.emit_event(&EventBuilder::new().label("claim-removed").with_claim(id, claim)?.build()) + } +} + +trait WithAllocation { + fn with_allocation( + self, + id: AllocationID, + alloc: &Allocation, + ) -> Result; +} + +impl WithAllocation for EventBuilder { + fn with_allocation( + self, + id: AllocationID, + alloc: &Allocation, + ) -> Result { + self.value_indexed("id", &id)? + .value_indexed("client", &alloc.client)? + .value_indexed("provider", &alloc.provider)? + .value_indexed("data-cid", &alloc.data)? + .value("data-size", &alloc.size)? + .value("term-min", &alloc.term_min)? + .value("term-max", &alloc.term_max)? + .value("expiration", &alloc.expiration) + } +} + +trait WithClaim { + fn with_claim(self, id: ClaimID, claim: &Claim) -> Result; +} + +impl WithClaim for EventBuilder { + fn with_claim(self, id: ClaimID, claim: &Claim) -> Result { + self.value_indexed("id", &id)? + .value_indexed("provider", &claim.provider)? + .value_indexed("client", &claim.client)? + .value_indexed("data-cid", &claim.data)? + .value("data-size", &claim.size)? + .value("term-min", &claim.term_min)? + .value("term-max", &claim.term_max)? + .value("term-start", &claim.term_start)? + .value("sector", &claim.sector) + } +} diff --git a/actors/verifreg/src/lib.rs b/actors/verifreg/src/lib.rs index d99cdb10dd..a4abb1fb30 100644 --- a/actors/verifreg/src/lib.rs +++ b/actors/verifreg/src/lib.rs @@ -6,6 +6,7 @@ use frc46_token::token::types::{BurnParams, TransferParams}; use frc46_token::token::TOKEN_PRECISION; use fvm_actor_utils::receiver::UniversalReceiverParams; use fvm_ipld_blockstore::Blockstore; +use fvm_ipld_encoding::ipld_block::IpldBlock; use fvm_ipld_encoding::RawBytes; use fvm_ipld_hamt::BytesKey; use fvm_shared::address::Address; @@ -14,6 +15,7 @@ use fvm_shared::bigint::BigInt; use fvm_shared::clock::ChainEpoch; use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; +use fvm_shared::sys::SendFlags; use fvm_shared::{ActorID, HAMT_BIT_WIDTH, METHOD_CONSTRUCTOR}; use log::info; use num_derive::FromPrimitive; @@ -29,9 +31,8 @@ use fil_actors_runtime::{ VERIFIED_REGISTRY_ACTOR_ADDR, }; use fil_actors_runtime::{ActorContext, AsActorError, BatchReturnGen}; -use fvm_ipld_encoding::ipld_block::IpldBlock; -use fvm_shared::sys::SendFlags; +use crate::events::Emit; use crate::ext::datacap::{DestroyParams, MintParams}; pub use self::state::Allocation; @@ -42,6 +43,7 @@ pub use self::types::*; #[cfg(feature = "fil-actor")] fil_actors_runtime::wasm_trampoline!(Actor); +pub mod events; pub mod expiration; pub mod ext; pub mod state; @@ -103,31 +105,32 @@ impl Actor { } let verifier = resolve_to_actor_id(rt, ¶ms.address, true)?; - let verifier = Address::new_id(verifier); + let verifier_addr = Address::new_id(verifier); let st: State = rt.state()?; rt.validate_immediate_caller_is(std::iter::once(&st.root_key))?; // Disallow root as a verifier. - if verifier == st.root_key { + if verifier_addr == st.root_key { return Err(actor_error!(illegal_argument, "Rootkey cannot be added as verifier")); } // Disallow existing clients as verifiers. - let token_balance = balance(rt, &verifier)?; + let token_balance = balance(rt, &verifier_addr)?; if token_balance.is_positive() { return Err(actor_error!( illegal_argument, "verified client {} cannot become a verifier", - verifier + verifier_addr )); } // Store the new verifier and allowance (over-writing). rt.transaction(|st: &mut State, rt| { - st.put_verifier(rt.store(), &verifier, ¶ms.allowance) + st.put_verifier(rt.store(), &verifier_addr, ¶ms.allowance) .context("failed to add verifier") - }) + })?; + Emit::verifier_balance(rt, verifier, ¶ms.allowance) } pub fn remove_verifier( @@ -135,12 +138,13 @@ impl Actor { params: RemoveVerifierParams, ) -> Result<(), ActorError> { let verifier = resolve_to_actor_id(rt, ¶ms.verifier, false)?; - let verifier = Address::new_id(verifier); + let verifier_addr = Address::new_id(verifier); rt.transaction(|st: &mut State, rt| { rt.validate_immediate_caller_is(std::iter::once(&st.root_key))?; - st.remove_verifier(rt.store(), &verifier).context("failed to remove verifier") - }) + st.remove_verifier(rt.store(), &verifier_addr).context("failed to remove verifier") + })?; + Emit::verifier_balance(rt, verifier, &DataCap::zero()) } pub fn add_verified_client( @@ -168,10 +172,11 @@ impl Actor { } // Validate caller is one of the verifiers, i.e. has an allowance (even if zero). - let verifier = rt.message().caller(); - let verifier_cap = st - .get_verifier_cap(rt.store(), &verifier)? - .ok_or_else(|| actor_error!(not_found, "caller {} is not a verifier", verifier))?; + let verifier_addr = rt.message().caller(); + let verifier_cap = + st.get_verifier_cap(rt.store(), &verifier_addr)?.ok_or_else(|| { + actor_error!(not_found, "caller {} is not a verifier", verifier_addr) + })?; // Disallow existing verifiers as clients. if st.get_verifier_cap(rt.store(), &client)?.is_some() { @@ -194,8 +199,9 @@ impl Actor { // Reduce verifier's cap. let new_verifier_cap = verifier_cap - ¶ms.allowance; - st.put_verifier(rt.store(), &verifier, &new_verifier_cap) - .context("failed to update verifier allowance") + st.put_verifier(rt.store(), &verifier_addr, &new_verifier_cap) + .context("failed to update verifier allowance")?; + Emit::verifier_balance(rt, verifier_addr.id().unwrap(), &new_verifier_cap) })?; // Credit client token allowance. @@ -335,12 +341,16 @@ impl Actor { } for id in to_remove { - let existing = allocs.remove(params.client, id).context_code( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to remove allocation {}", id), - )?; - // Unwrapping here as both paths to here should ensure the allocation exists. - recovered_datacap += existing.unwrap().size.0; + let existing = allocs + .remove(params.client, id) + .context_code( + ExitCode::USR_ILLEGAL_STATE, + format!("failed to remove allocation {}", id), + )? + // Unwrapping here as both paths should ensure the allocation exists. + .unwrap(); + Emit::allocation_removed(rt, id, &existing)?; + recovered_datacap += existing.size.0; } st.save_allocs(&mut allocs)?; @@ -426,7 +436,7 @@ impl Actor { }; let inserted = claims - .put_if_absent(provider, claim_alloc.allocation_id, new_claim) + .put_if_absent(provider, claim_alloc.allocation_id, new_claim.clone()) .context_code( ExitCode::USR_ILLEGAL_STATE, format!("failed to write claim {}", claim_alloc.allocation_id), @@ -440,6 +450,7 @@ impl Actor { sector_claims.push(SectorAllocationClaimResult::default()); continue; } + Emit::claim(rt, claim_alloc.allocation_id, &new_claim)?; allocs.remove(claim_alloc.client, claim_alloc.allocation_id).context_code( ExitCode::USR_ILLEGAL_STATE, @@ -547,11 +558,12 @@ impl Actor { } let new_claim = Claim { term_max: term.term_max, ..*claim }; - st_claims.put(term.provider, term.claim_id, new_claim).context_code( + st_claims.put(term.provider, term.claim_id, new_claim.clone()).context_code( ExitCode::USR_ILLEGAL_STATE, "HAMT put failure storing new claims", )?; batch_gen.add_success(); + Emit::claim_updated(rt, term.claim_id, &new_claim)?; } else { batch_gen.add_fail(ExitCode::USR_NOT_FOUND); info!("no claim {} for provider {}", term.claim_id, term.provider); @@ -595,10 +607,15 @@ impl Actor { } for id in to_remove { - claims.remove(params.provider, id).context_code( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to remove claim {}", id), - )?; + let removed = claims + .remove(params.provider, id) + .context_code( + ExitCode::USR_ILLEGAL_STATE, + format!("failed to remove claim {}", id), + )? + // Unwrapping here as all paths should ensure the claim exists. + .unwrap(); + Emit::claim_removed(rt, id, &removed)?; } st.save_claims(&mut claims)?; @@ -695,8 +712,14 @@ impl Actor { // Save new allocations and updated claims. let ids = rt.transaction(|st: &mut State, rt| { - let ids = st.insert_allocations(rt.store(), client, new_allocs)?; - st.put_claims(rt.store(), updated_claims)?; + let ids = st.insert_allocations(rt.store(), client, new_allocs.clone())?; + for (id, alloc) in ids.iter().zip(new_allocs.iter()) { + Emit::allocation(rt, *id, alloc)?; + } + st.put_claims(rt.store(), updated_claims.clone())?; + for (id, claim) in updated_claims { + Emit::claim_updated(rt, id, &claim)?; + } Ok(ids) })?; diff --git a/actors/verifreg/tests/harness/mod.rs b/actors/verifreg/tests/harness/mod.rs index 61b49e9022..7f774db245 100644 --- a/actors/verifreg/tests/harness/mod.rs +++ b/actors/verifreg/tests/harness/mod.rs @@ -1,3 +1,22 @@ +use std::cell::RefCell; +use std::collections::HashMap; + +use frc46_token::receiver::{FRC46TokenReceived, FRC46_TOKEN_TYPE}; +use frc46_token::token::types::{BurnParams, BurnReturn, TransferParams}; +use frc46_token::token::TOKEN_PRECISION; +use fvm_actor_utils::receiver::UniversalReceiverParams; +use fvm_ipld_encoding::ipld_block::IpldBlock; +use fvm_ipld_encoding::RawBytes; +use fvm_shared::address::Address; +use fvm_shared::bigint::bigint_ser::{BigIntDe, BigIntSer}; +use fvm_shared::clock::ChainEpoch; +use fvm_shared::econ::TokenAmount; +use fvm_shared::error::ExitCode; +use fvm_shared::piece::PaddedPieceSize; +use fvm_shared::sector::SectorNumber; +use fvm_shared::{ActorID, MethodNum, HAMT_BIT_WIDTH}; +use num_traits::{ToPrimitive, Zero}; + use fil_actor_verifreg::testing::check_state_invariants; use fil_actor_verifreg::{ ext, Actor as VerifregActor, AddVerifiedClientParams, AddVerifierParams, Allocation, @@ -15,26 +34,9 @@ use fil_actors_runtime::runtime::policy_constants::{ use fil_actors_runtime::runtime::Runtime; use fil_actors_runtime::test_utils::*; use fil_actors_runtime::{ - make_empty_map, ActorError, AsActorError, BatchReturn, DATACAP_TOKEN_ACTOR_ADDR, + make_empty_map, ActorError, AsActorError, BatchReturn, EventBuilder, DATACAP_TOKEN_ACTOR_ADDR, STORAGE_MARKET_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, VERIFIED_REGISTRY_ACTOR_ADDR, }; -use frc46_token::receiver::{FRC46TokenReceived, FRC46_TOKEN_TYPE}; -use frc46_token::token::types::{BurnParams, BurnReturn, TransferParams}; -use frc46_token::token::TOKEN_PRECISION; -use fvm_actor_utils::receiver::UniversalReceiverParams; -use fvm_ipld_encoding::ipld_block::IpldBlock; -use fvm_ipld_encoding::RawBytes; -use fvm_shared::address::Address; -use fvm_shared::bigint::bigint_ser::{BigIntDe, BigIntSer}; -use fvm_shared::clock::ChainEpoch; -use fvm_shared::econ::TokenAmount; -use fvm_shared::error::ExitCode; -use fvm_shared::piece::PaddedPieceSize; -use fvm_shared::sector::SectorNumber; -use fvm_shared::{ActorID, MethodNum, HAMT_BIT_WIDTH}; -use num_traits::{ToPrimitive, Zero}; -use std::cell::RefCell; -use std::collections::HashMap; pub const ROOT_ADDR: Address = Address::new_id(101); @@ -132,6 +134,13 @@ impl Harness { IpldBlock::serialize_cbor(&BigIntSer(&(cap * TOKEN_PRECISION))).unwrap(), ExitCode::OK, ); + rt.expect_emitted_event( + EventBuilder::new() + .label("verifier-balance") + .value_indexed("verifier", &verifier_resolved.id().unwrap())? + .value("balance", &allowance)? + .build(), + ); let params = AddVerifierParams { address: *verifier, allowance: allowance.clone() }; let ret = rt.call::( @@ -147,6 +156,14 @@ impl Harness { pub fn remove_verifier(&self, rt: &MockRuntime, verifier: &Address) -> Result<(), ActorError> { rt.expect_validate_caller_addr(vec![self.root]); + rt.expect_emitted_event( + EventBuilder::new() + .label("verifier-balance") + .value_indexed("verifier", &verifier.id().unwrap())? + .value("balance", &DataCap::zero())? + .build(), + ); + rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, self.root); let ret = rt.call::( Method::RemoveVerifier as MethodNum, @@ -187,6 +204,7 @@ impl Harness { verifier: &Address, client: &Address, allowance: &DataCap, + verifier_balance: &DataCap, ) -> Result<(), ActorError> { rt.expect_validate_caller_any(); rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, *verifier); @@ -208,6 +226,13 @@ impl Harness { ); let params = AddVerifiedClientParams { address: *client, allowance: allowance.clone() }; + rt.expect_emitted_event( + EventBuilder::new() + .label("verifier-balance") + .value_indexed("verifier", &verifier.id().unwrap())? + .value("balance", &(verifier_balance - allowance))? + .build(), + ); let ret = rt.call::( Method::AddVerifiedClient as MethodNum, IpldBlock::serialize_cbor(¶ms).unwrap(), @@ -258,18 +283,39 @@ impl Harness { rt: &MockRuntime, provider: ActorID, claim_allocs: Vec, - datacap_burnt: u64, + expect_claimed: Vec<(AllocationID, Allocation)>, all_or_nothing: bool, ) -> Result { - rt.expect_validate_caller_type(vec![Type::Miner]); - rt.set_caller(*MINER_ACTOR_CODE_ID, Address::new_id(provider)); + let epoch = *rt.epoch.borrow(); + let mut expected_datacap = 0u64; + let expected: HashMap = expect_claimed.into_iter().collect(); + for req in claim_allocs.iter() { + if let Some(alloc) = expected.get(&req.allocation_id) { + expected_datacap += alloc.size.0; + let claim = claim_from_alloc(alloc, epoch, req.sector); + rt.expect_emitted_event( + EventBuilder::new() + .label("claim") + .value_indexed("id", &req.allocation_id)? + .value_indexed("provider", &alloc.provider)? + .value_indexed("client", &alloc.client)? + .value_indexed("data-cid", &alloc.data)? + .value("data-size", &alloc.size)? + .value("term-min", &alloc.term_min)? + .value("term-max", &alloc.term_max)? + .value("term-start", &epoch)? + .value("sector", &claim.sector)? + .build(), + ); + } + } - if datacap_burnt > 0 { + if expected_datacap > 0 { rt.expect_send_simple( DATACAP_TOKEN_ACTOR_ADDR, ext::datacap::Method::Burn as MethodNum, IpldBlock::serialize_cbor(&BurnParams { - amount: TokenAmount::from_whole(datacap_burnt.to_i64().unwrap()), + amount: TokenAmount::from_whole(expected_datacap.to_i64().unwrap()), }) .unwrap(), TokenAmount::zero(), @@ -278,6 +324,8 @@ impl Harness { ); } + rt.expect_validate_caller_type(vec![Type::Miner]); + rt.set_caller(*MINER_ACTOR_CODE_ID, Address::new_id(provider)); let params = ClaimAllocationsParams { allocations: claim_allocs, all_or_nothing }; let ret = rt .call::( @@ -287,7 +335,6 @@ impl Harness { .unwrap() .deserialize() .expect("failed to deserialize claim allocations return"); - rt.verify(); Ok(ret) } @@ -297,10 +344,26 @@ impl Harness { rt: &MockRuntime, client: ActorID, allocation_ids: Vec, - expected_datacap: u64, + expect_removed: Vec<(AllocationID, Allocation)>, ) -> Result { rt.expect_validate_caller_any(); - + let mut expected_datacap = 0u64; + for (id, alloc) in expect_removed { + expected_datacap += alloc.size.0; + rt.expect_emitted_event( + EventBuilder::new() + .label("allocation-removed") + .value_indexed("id", &id)? + .value_indexed("client", &alloc.client)? + .value_indexed("provider", &alloc.provider)? + .value_indexed("data-cid", &alloc.data)? + .value("data-size", &alloc.size)? + .value("term-min", &alloc.term_min)? + .value("term-max", &alloc.term_max)? + .value("expiration", &alloc.expiration)? + .build(), + ); + } rt.expect_send_simple( DATACAP_TOKEN_ACTOR_ADDR, ext::datacap::Method::Transfer as MethodNum, @@ -309,7 +372,7 @@ impl Harness { amount: TokenAmount::from_whole(expected_datacap.to_i64().unwrap()), operator_data: RawBytes::default(), }) - .unwrap(), + .unwrap(), TokenAmount::zero(), None, ExitCode::OK, @@ -334,9 +397,27 @@ impl Harness { rt: &MockRuntime, provider: ActorID, claim_ids: Vec, + expecte_removed: Vec<(ClaimID, Claim)>, ) -> Result { rt.expect_validate_caller_any(); + for (id, claim) in expecte_removed { + rt.expect_emitted_event( + EventBuilder::new() + .label("claim-removed") + .value_indexed("id", &id)? + .value_indexed("provider", &claim.provider)? + .value_indexed("client", &claim.client)? + .value_indexed("data-cid", &claim.data)? + .value("data-size", &claim.size)? + .value("term-min", &claim.term_min)? + .value("term-max", &claim.term_max)? + .value("term-start", &claim.term_start)? + .value("sector", &claim.sector)? + .build(), + ); + } + let params = RemoveExpiredClaimsParams { provider, claim_ids }; let ret = rt .call::( @@ -385,6 +466,40 @@ impl Harness { ); } + let allocs_req: AllocationRequests = payload.operator_data.deserialize().unwrap(); + for (alloc, id) in allocs_req.allocations.iter().zip(expected_alloc_ids.iter()) { + rt.expect_emitted_event( + EventBuilder::new() + .label("allocation") + .value_indexed("id", &id)? + .value_indexed("client", &payload.from)? + .value_indexed("provider", &alloc.provider)? + .value_indexed("data-cid", &alloc.data)? + .value("data-size", &alloc.size)? + .value("term-min", &alloc.term_min)? + .value("term-max", &alloc.term_max)? + .value("expiration", &alloc.expiration)? + .build(), + ); + } + for ext in allocs_req.extensions { + let claim = self.load_claim(rt, ext.provider, ext.claim).unwrap(); + rt.expect_emitted_event( + EventBuilder::new() + .label("claim-updated") + .value_indexed("id", &ext.claim)? + .value_indexed("provider", &claim.provider)? + .value_indexed("client", &claim.client)? + .value_indexed("data-cid", &claim.data)? + .value("data-size", &claim.size)? + .value("term-min", &claim.term_min)? + .value("term-max", &ext.term_max)? // From request + .value("term-start", &claim.term_start)? + .value("sector", &claim.sector)? + .build(), + ); + } + rt.expect_validate_caller_addr(vec![DATACAP_TOKEN_ACTOR_ADDR]); let ret = rt.call::( Method::UniversalReceiverHook as MethodNum, @@ -440,7 +555,24 @@ impl Harness { &self, rt: &MockRuntime, params: &ExtendClaimTermsParams, + expected: Vec<(ClaimID, Claim)>, ) -> Result { + for (id, new_claim) in expected.iter() { + rt.expect_emitted_event( + EventBuilder::new() + .label("claim-updated") + .value_indexed("id", &id)? + .value_indexed("provider", &new_claim.provider)? + .value_indexed("client", &new_claim.client)? + .value_indexed("data-cid", &new_claim.data)? + .value("data-size", &new_claim.size)? + .value("term-min", &new_claim.term_min)? + .value("term-max", &new_claim.term_max)? + .value("term-start", &new_claim.term_start)? + .value("sector", &new_claim.sector)? + .build(), + ); + } rt.expect_validate_caller_any(); let ret = rt .call::( @@ -450,7 +582,11 @@ impl Harness { .unwrap() .deserialize() .expect("failed to deserialize extend claim terms return"); - rt.verify(); + + for (id, expected_claim) in expected { + let claim = self.load_claim(rt, expected_claim.provider, id).unwrap(); + assert_eq!(expected_claim, claim); + } Ok(ret) } } diff --git a/actors/verifreg/tests/verifreg_actor_test.rs b/actors/verifreg/tests/verifreg_actor_test.rs index b1c3366245..38afb4f779 100644 --- a/actors/verifreg/tests/verifreg_actor_test.rs +++ b/actors/verifreg/tests/verifreg_actor_test.rs @@ -15,11 +15,12 @@ lazy_static! { } mod util { - use fil_actor_verifreg::ClaimAllocationsReturn; - use fil_actors_runtime::test_utils::MockRuntime; use fvm_shared::{bigint::BigInt, sector::StoragePower}; use num_traits::Zero; + use fil_actor_verifreg::ClaimAllocationsReturn; + use fil_actors_runtime::test_utils::MockRuntime; + pub fn verifier_allowance(rt: &MockRuntime) -> StoragePower { rt.policy.minimum_verified_allocation_size.clone() + 42 } @@ -38,6 +39,7 @@ mod util { } mod construction { + use fvm_ipld_encoding::ipld_block::IpldBlock; use fvm_shared::address::{Address, BLS_PUB_LEN}; use fvm_shared::error::ExitCode; use fvm_shared::MethodNum; @@ -45,7 +47,6 @@ mod construction { use fil_actor_verifreg::{Actor as VerifregActor, Method}; use fil_actors_runtime::test_utils::*; use fil_actors_runtime::SYSTEM_ACTOR_ADDR; - use fvm_ipld_encoding::ipld_block::IpldBlock; use harness::*; use crate::*; @@ -86,15 +87,16 @@ mod construction { } mod verifiers { + use std::ops::Deref; + + use fvm_ipld_encoding::ipld_block::IpldBlock; use fvm_shared::address::{Address, BLS_PUB_LEN}; use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; use fvm_shared::{MethodNum, METHOD_SEND}; - use std::ops::Deref; use fil_actor_verifreg::{Actor as VerifregActor, AddVerifierParams, DataCap, Method}; use fil_actors_runtime::test_utils::*; - use fvm_ipld_encoding::ipld_block::IpldBlock; use harness::*; use util::*; @@ -149,6 +151,7 @@ mod verifiers { h.add_verifier_with_existing_cap(&rt, &VERIFIER, &allowance, &DataCap::from(1)), ); h.check_state(&rt); + rt.reset(); } #[test] @@ -220,6 +223,7 @@ mod verifiers { let (h, rt) = new_harness(); expect_abort(ExitCode::USR_ILLEGAL_ARGUMENT, h.remove_verifier(&rt, &VERIFIER)); h.check_state(&rt); + rt.reset(); } #[test] @@ -246,21 +250,20 @@ mod verifiers { } mod clients { + use fvm_ipld_encoding::ipld_block::IpldBlock; use fvm_shared::address::{Address, BLS_PUB_LEN}; use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; use fvm_shared::{MethodNum, METHOD_SEND}; + use num_traits::ToPrimitive; use num_traits::Zero; use fil_actor_verifreg::{ ext, Actor as VerifregActor, AddVerifiedClientParams, DataCap, Method, }; use fil_actors_runtime::test_utils::*; - use fil_actors_runtime::{DATACAP_TOKEN_ACTOR_ADDR, STORAGE_MARKET_ACTOR_ADDR}; - - use fvm_ipld_encoding::ipld_block::IpldBlock; + use fil_actors_runtime::{EventBuilder, DATACAP_TOKEN_ACTOR_ADDR, STORAGE_MARKET_ACTOR_ADDR}; use harness::*; - use num_traits::ToPrimitive; use util::*; use crate::*; @@ -274,11 +277,25 @@ mod clients { h.add_verifier(&rt, &VERIFIER, &allowance_verifier).unwrap(); h.add_verifier(&rt, &VERIFIER2, &allowance_verifier).unwrap(); - h.add_client(&rt, &VERIFIER, &CLIENT, &allowance_client).unwrap(); - h.add_client(&rt, &VERIFIER, &CLIENT2, &allowance_client).unwrap(); + h.add_client(&rt, &VERIFIER, &CLIENT, &allowance_client, &allowance_verifier).unwrap(); + h.add_client( + &rt, + &VERIFIER, + &CLIENT2, + &allowance_client, + &(&allowance_verifier - &allowance_client), + ) + .unwrap(); - h.add_client(&rt, &VERIFIER2, &CLIENT3, &allowance_client).unwrap(); - h.add_client(&rt, &VERIFIER2, &CLIENT4, &allowance_client).unwrap(); + h.add_client(&rt, &VERIFIER2, &CLIENT3, &allowance_client, &allowance_verifier).unwrap(); + h.add_client( + &rt, + &VERIFIER2, + &CLIENT4, + &allowance_client, + &(&allowance_verifier - &allowance_client), + ) + .unwrap(); // No more allowance left h.assert_verifier_allowance(&rt, &VERIFIER, &DataCap::from(0)); @@ -293,10 +310,10 @@ mod clients { // Verifier only has allowance for one client. h.add_verifier(&rt, &VERIFIER, &allowance).unwrap(); - h.add_client(&rt, &VERIFIER, &CLIENT, &allowance).unwrap(); + h.add_client(&rt, &VERIFIER, &CLIENT, &allowance, &allowance).unwrap(); expect_abort( ExitCode::USR_ILLEGAL_ARGUMENT, - h.add_client(&rt, &VERIFIER, &CLIENT2, &allowance), + h.add_client(&rt, &VERIFIER, &CLIENT2, &allowance, &DataCap::zero()), ); rt.reset(); h.assert_verifier_allowance(&rt, &VERIFIER, &DataCap::zero()); @@ -313,12 +330,13 @@ mod clients { rt.id_addresses.borrow_mut().insert(client_pubkey, *CLIENT); h.add_verifier(&rt, &VERIFIER, &allowance_verifier).unwrap(); - h.add_client(&rt, &VERIFIER, &client_pubkey, &allowance_client).unwrap(); + h.add_client(&rt, &VERIFIER, &client_pubkey, &allowance_client, &allowance_verifier) + .unwrap(); // Adding another client with the same address increments // the data cap which has already been granted. h.add_verifier(&rt, &VERIFIER, &allowance_verifier).unwrap(); - h.add_client(&rt, &VERIFIER, &CLIENT, &allowance_client).unwrap(); + h.add_client(&rt, &VERIFIER, &CLIENT, &allowance_client, &allowance_verifier).unwrap(); h.check_state(&rt); } @@ -329,7 +347,7 @@ mod clients { h.add_verifier(&rt, &VERIFIER, &allowance_verifier).unwrap(); let allowance = rt.policy.minimum_verified_allocation_size.clone(); - h.add_client(&rt, &VERIFIER, &CLIENT, &allowance).unwrap(); + h.add_client(&rt, &VERIFIER, &CLIENT, &allowance, &allowance_verifier).unwrap(); h.check_state(&rt); } @@ -354,7 +372,7 @@ mod clients { expect_abort( ExitCode::USR_ILLEGAL_ARGUMENT, - h.add_client(&rt, &VERIFIER, &client, &allowance_client), + h.add_client(&rt, &VERIFIER, &client, &allowance_client, &allowance_verifier), ); rt.reset(); h.check_state(&rt); @@ -369,7 +387,7 @@ mod clients { let allowance = rt.policy.minimum_verified_allocation_size.clone() - 1; expect_abort( ExitCode::USR_ILLEGAL_ARGUMENT, - h.add_client(&rt, &VERIFIER, &CLIENT, &allowance), + h.add_client(&rt, &VERIFIER, &CLIENT, &allowance, &allowance_verifier), ); rt.reset(); h.check_state(&rt); @@ -418,8 +436,7 @@ mod clients { IpldBlock::serialize_cbor(¶ms).unwrap(), ), ); - - rt.verify(); + rt.reset(); // can call the exported method num @@ -436,7 +453,15 @@ mod clients { None, ExitCode::OK, ); - + rt.expect_emitted_event( + EventBuilder::new() + .label("verifier-balance") + .value_indexed("verifier", &VERIFIER.id().unwrap()) + .unwrap() + .value("balance", &(allowance_verifier - allowance_client)) + .unwrap() + .build(), + ); rt.expect_validate_caller_any(); rt.call::( Method::AddVerifiedClientExported as MethodNum, @@ -445,7 +470,6 @@ mod clients { .unwrap(); rt.verify(); - h.check_state(&rt); } @@ -455,10 +479,10 @@ mod clients { let allowance_verifier = verifier_allowance(&rt); h.add_verifier(&rt, &VERIFIER, &allowance_verifier).unwrap(); - let allowance = allowance_verifier + 1; + let allowance = &allowance_verifier + 1; expect_abort( ExitCode::USR_ILLEGAL_ARGUMENT, - h.add_client(&rt, &VERIFIER, &h.root, &allowance), + h.add_client(&rt, &VERIFIER, &h.root, &allowance, &allowance_verifier), ); rt.reset(); h.check_state(&rt); @@ -472,7 +496,7 @@ mod clients { h.add_verifier(&rt, &VERIFIER, &allowance_verifier).unwrap(); expect_abort( ExitCode::USR_ILLEGAL_ARGUMENT, - h.add_client(&rt, &VERIFIER, &h.root, &allowance_client), + h.add_client(&rt, &VERIFIER, &h.root, &allowance_client, &allowance_verifier), ); rt.reset(); h.check_state(&rt); @@ -486,14 +510,14 @@ mod clients { h.add_verifier(&rt, &VERIFIER, &allowance_verifier).unwrap(); expect_abort( ExitCode::USR_ILLEGAL_ARGUMENT, - h.add_client(&rt, &VERIFIER, &VERIFIER, &allowance_client), + h.add_client(&rt, &VERIFIER, &VERIFIER, &allowance_client, &allowance_verifier), ); rt.reset(); h.add_verifier(&rt, &VERIFIER2, &allowance_verifier).unwrap(); expect_abort( ExitCode::USR_ILLEGAL_ARGUMENT, - h.add_client(&rt, &VERIFIER, &VERIFIER2, &allowance_client), + h.add_client(&rt, &VERIFIER, &VERIFIER2, &allowance_client, &allowance_verifier), ); rt.reset(); h.check_state(&rt); @@ -501,6 +525,8 @@ mod clients { } mod allocs_claims { + use std::str::FromStr; + use cid::Cid; use fvm_ipld_encoding::ipld_block::IpldBlock; use fvm_shared::bigint::BigInt; @@ -508,13 +534,12 @@ mod allocs_claims { use fvm_shared::piece::PaddedPieceSize; use fvm_shared::{ActorID, MethodNum}; use num_traits::Zero; - use std::str::FromStr; + use fil_actor_verifreg::Claim; use fil_actor_verifreg::{ - Actor, AllocationID, ClaimTerm, DataCap, ExtendClaimTermsParams, GetClaimsParams, - GetClaimsReturn, Method, State, + Actor, AllocationID, ClaimTerm, DataCap, ExtendClaimTermsParams, ExtendClaimTermsReturn, + GetClaimsParams, Method, State, }; - use fil_actor_verifreg::{Claim, ExtendClaimTermsReturn}; use fil_actors_runtime::runtime::policy_constants::{ MAXIMUM_VERIFIED_ALLOCATION_TERM, MINIMUM_VERIFIED_ALLOCATION_SIZE, MINIMUM_VERIFIED_ALLOCATION_TERM, @@ -548,15 +573,19 @@ mod allocs_claims { let id2 = h.create_alloc(&rt, &alloc2).unwrap(); let state_with_allocs: State = rt.get_state(); + let expect_1 = vec![(id1, alloc1.clone())]; + let expect_2 = vec![(id2, alloc2.clone())]; + let expect_both = vec![(id1, alloc1.clone()), (id2, alloc2.clone())]; + // Can't remove allocations that aren't expired - let ret = h.remove_expired_allocations(&rt, CLIENT1, vec![id1, id2], 0).unwrap(); + let ret = h.remove_expired_allocations(&rt, CLIENT1, vec![id1, id2], vec![]).unwrap(); assert_eq!(vec![1, 2], ret.considered); assert_eq!(vec![ExitCode::USR_FORBIDDEN, ExitCode::USR_FORBIDDEN], ret.results.codes()); assert_eq!(DataCap::zero(), ret.datacap_recovered); // Can't remove with wrong client ID rt.set_epoch(200); - let ret = h.remove_expired_allocations(&rt, CLIENT2, vec![id1, id2], 0).unwrap(); + let ret = h.remove_expired_allocations(&rt, CLIENT2, vec![id1, id2], vec![]).unwrap(); assert_eq!(vec![1, 2], ret.considered); assert_eq!(vec![ExitCode::USR_NOT_FOUND, ExitCode::USR_NOT_FOUND], ret.results.codes()); assert_eq!(DataCap::zero(), ret.datacap_recovered); @@ -564,7 +593,7 @@ mod allocs_claims { // Remove the first alloc, which expired. rt.set_epoch(100); let ret = - h.remove_expired_allocations(&rt, CLIENT1, vec![id1, id2], alloc1.size.0).unwrap(); + h.remove_expired_allocations(&rt, CLIENT1, vec![id1, id2], expect_1.clone()).unwrap(); assert_eq!(vec![1, 2], ret.considered); assert_eq!(vec![ExitCode::OK, ExitCode::USR_FORBIDDEN], ret.results.codes()); assert_eq!(DataCap::from(alloc1.size.0), ret.datacap_recovered); @@ -572,21 +601,23 @@ mod allocs_claims { // Remove the second alloc (the first is no longer found). rt.set_epoch(200); let ret = - h.remove_expired_allocations(&rt, CLIENT1, vec![id1, id2], alloc2.size.0).unwrap(); + h.remove_expired_allocations(&rt, CLIENT1, vec![id1, id2], expect_2.clone()).unwrap(); assert_eq!(vec![1, 2], ret.considered); assert_eq!(vec![ExitCode::USR_NOT_FOUND, ExitCode::OK], ret.results.codes()); assert_eq!(DataCap::from(alloc2.size.0), ret.datacap_recovered); // Reset state and show we can remove two at once. rt.replace_state(&state_with_allocs); - let ret = h.remove_expired_allocations(&rt, CLIENT1, vec![id1, id2], total_size).unwrap(); + let ret = h + .remove_expired_allocations(&rt, CLIENT1, vec![id1, id2], expect_both) + .unwrap(); assert_eq!(vec![1, 2], ret.considered); assert_eq!(vec![ExitCode::OK, ExitCode::OK], ret.results.codes()); assert_eq!(DataCap::from(total_size), ret.datacap_recovered); // Reset state and show that only what was asked for is removed. rt.replace_state(&state_with_allocs); - let ret = h.remove_expired_allocations(&rt, CLIENT1, vec![id1], alloc1.size.0).unwrap(); + let ret = h.remove_expired_allocations(&rt, CLIENT1, vec![id1], expect_1.clone()).unwrap(); assert_eq!(vec![1], ret.considered); assert_eq!(vec![ExitCode::OK], ret.results.codes()); assert_eq!(DataCap::from(alloc1.size.0), ret.datacap_recovered); @@ -594,7 +625,7 @@ mod allocs_claims { // Reset state and show that specifying none removes only expired allocations rt.set_epoch(0); rt.replace_state(&state_with_allocs); - let ret = h.remove_expired_allocations(&rt, CLIENT1, vec![], 0).unwrap(); + let ret = h.remove_expired_allocations(&rt, CLIENT1, vec![], vec![]).unwrap(); assert_eq!(Vec::::new(), ret.considered); assert_eq!(Vec::::new(), ret.results.codes()); assert_eq!(DataCap::zero(), ret.datacap_recovered); @@ -602,7 +633,7 @@ mod allocs_claims { assert!(h.load_alloc(&rt, CLIENT1, id2).is_some()); rt.set_epoch(100); - let ret = h.remove_expired_allocations(&rt, CLIENT1, vec![], alloc1.size.0).unwrap(); + let ret = h.remove_expired_allocations(&rt, CLIENT1, vec![], expect_1).unwrap(); assert_eq!(vec![1], ret.considered); assert_eq!(vec![ExitCode::OK], ret.results.codes()); assert_eq!(DataCap::from(alloc1.size.0), ret.datacap_recovered); @@ -610,7 +641,7 @@ mod allocs_claims { assert!(h.load_alloc(&rt, CLIENT1, id2).is_some()); rt.set_epoch(200); - let ret = h.remove_expired_allocations(&rt, CLIENT1, vec![], alloc2.size.0).unwrap(); + let ret = h.remove_expired_allocations(&rt, CLIENT1, vec![], expect_2).unwrap(); assert_eq!(vec![2], ret.considered); assert_eq!(vec![ExitCode::OK], ret.results.codes()); assert_eq!(DataCap::from(alloc2.size.0), ret.datacap_recovered); @@ -619,7 +650,9 @@ mod allocs_claims { // Reset state and show that specifying none removes *all* expired allocations rt.replace_state(&state_with_allocs); - let ret = h.remove_expired_allocations(&rt, CLIENT1, vec![], total_size).unwrap(); + let ret = h + .remove_expired_allocations(&rt, CLIENT1, vec![], vec![(id1, alloc1), (id2, alloc2)]) + .unwrap(); assert_eq!(vec![1, 2], ret.considered); assert_eq!(vec![ExitCode::OK, ExitCode::OK], ret.results.codes()); assert_eq!(DataCap::from(total_size), ret.datacap_recovered); @@ -637,9 +670,9 @@ mod allocs_claims { let alloc2 = make_alloc("2", CLIENT2, PROVIDER1, size); // Distinct client let alloc3 = make_alloc("3", CLIENT1, PROVIDER2, size); // Distinct provider - h.create_alloc(&rt, &alloc1).unwrap(); - h.create_alloc(&rt, &alloc2).unwrap(); - h.create_alloc(&rt, &alloc3).unwrap(); + let id1 = h.create_alloc(&rt, &alloc1).unwrap(); + let id2 = h.create_alloc(&rt, &alloc2).unwrap(); + let _id3 = h.create_alloc(&rt, &alloc3).unwrap(); h.check_state(&rt); let sector = 1000; @@ -649,25 +682,36 @@ mod allocs_claims { { // Claim two for PROVIDER1 let reqs = vec![ - make_claim_req(1, &alloc1, sector, expiry), - make_claim_req(2, &alloc2, sector, expiry), + make_claim_req(id1, &alloc1, sector, expiry), + make_claim_req(id2, &alloc2, sector, expiry), ]; - let ret = h.claim_allocations(&rt, PROVIDER1, reqs, size * 2, false).unwrap(); + let ret = h + .claim_allocations( + &rt, + PROVIDER1, + reqs, + vec![(id1, alloc1.clone()), (id2, alloc2.clone())], + false, + ) + .unwrap(); assert_eq!(ret.claim_results.len(), 2); assert_eq!(total_claimed_space(&ret), BigInt::from(2 * size)); assert_alloc_claimed(&rt, CLIENT1, PROVIDER1, 1, &alloc1, 0, sector); assert_alloc_claimed(&rt, CLIENT2, PROVIDER1, 2, &alloc2, 0, sector); h.check_state(&rt); + rt.verify(); } { // Can't find claim for wrong client rt.replace_state(&prior_state); let mut reqs = vec![ - make_claim_req(1, &alloc1, sector, expiry), - make_claim_req(2, &alloc2, sector, expiry), + make_claim_req(id1, &alloc1, sector, expiry), + make_claim_req(id2, &alloc2, sector, expiry), ]; reqs[1].client = CLIENT1; - let ret = h.claim_allocations(&rt, PROVIDER1, reqs, size, false).unwrap(); + let ret = h + .claim_allocations(&rt, PROVIDER1, reqs, vec![(id1, alloc1.clone())], false) + .unwrap(); assert_eq!(ret.claim_results.len(), 2); assert!(!ret.claim_results[0].claimed_space.is_zero()); assert!(ret.claim_results[1].claimed_space.is_zero()); @@ -675,6 +719,7 @@ mod allocs_claims { assert_alloc_claimed(&rt, CLIENT1, PROVIDER1, 1, &alloc1, 0, sector); assert_allocation(&rt, CLIENT2, 2, &alloc2); h.check_state(&rt); + rt.verify(); } { // Can't claim for other provider @@ -683,7 +728,9 @@ mod allocs_claims { make_claim_req(2, &alloc2, sector, expiry), make_claim_req(3, &alloc3, sector, expiry), // Different provider ]; - let ret = h.claim_allocations(&rt, PROVIDER1, reqs, size, false).unwrap(); + let ret = h + .claim_allocations(&rt, PROVIDER1, reqs, vec![(id2, alloc2.clone())], false) + .unwrap(); assert_eq!(ret.claim_results.len(), 2); assert!(!ret.claim_results[0].claimed_space.is_zero()); assert!(ret.claim_results[1].claimed_space.is_zero()); @@ -691,6 +738,7 @@ mod allocs_claims { assert_alloc_claimed(&rt, CLIENT1, PROVIDER1, 2, &alloc2, 0, sector); assert_allocation(&rt, CLIENT1, 3, &alloc3); h.check_state(&rt); + rt.verify(); } { // Mismatched data / size @@ -703,37 +751,41 @@ mod allocs_claims { Cid::from_str("bafyreibjo4xmgaevkgud7mbifn3dzp4v4lyaui4yvqp3f2bqwtxcjrdqg4") .unwrap(); reqs[1].size = PaddedPieceSize(size + 1); - let ret = h.claim_allocations(&rt, PROVIDER1, reqs, 0, false).unwrap(); + let ret = h.claim_allocations(&rt, PROVIDER1, reqs, vec![], false).unwrap(); assert_eq!(ret.claim_results.len(), 2); assert!(ret.claim_results[0].claimed_space.is_zero()); assert!(ret.claim_results[1].claimed_space.is_zero()); assert_eq!(total_claimed_space(&ret), BigInt::zero()); h.check_state(&rt); + rt.verify(); } { // Expired allocation rt.replace_state(&prior_state); let reqs = vec![make_claim_req(1, &alloc1, sector, expiry)]; rt.set_epoch(alloc1.expiration + 1); - let ret = h.claim_allocations(&rt, PROVIDER1, reqs, 0, false).unwrap(); + let ret = h.claim_allocations(&rt, PROVIDER1, reqs, vec![], false).unwrap(); assert_eq!(ret.claim_results.len(), 1); assert_eq!(total_claimed_space(&ret), BigInt::zero()); h.check_state(&rt); + rt.verify(); } { // Sector expiration too soon rt.replace_state(&prior_state); let reqs = vec![make_claim_req(1, &alloc1, sector, alloc1.term_min - 1)]; - let ret = h.claim_allocations(&rt, PROVIDER1, reqs, 0, false).unwrap(); + let ret = h.claim_allocations(&rt, PROVIDER1, reqs, vec![], false).unwrap(); assert_eq!(ret.claim_results.len(), 1); assert_eq!(total_claimed_space(&ret), BigInt::zero()); + rt.verify(); // Sector expiration too late let reqs = vec![make_claim_req(1, &alloc1, sector, alloc1.term_max + 1)]; - let ret = h.claim_allocations(&rt, PROVIDER1, reqs, 0, false).unwrap(); + let ret = h.claim_allocations(&rt, PROVIDER1, reqs, vec![], false).unwrap(); assert_eq!(ret.claim_results.len(), 1); assert_eq!(total_claimed_space(&ret), BigInt::zero()); h.check_state(&rt); + rt.verify(); } } @@ -804,14 +856,14 @@ mod allocs_claims { ClaimTerm { provider: PROVIDER2, claim_id: id3, term_max: max_term + 3 }, ], }; + let expected_claims = vec![ + (id1, Claim { term_max: max_term + 1, ..claim1 }), + (id2, Claim { term_max: max_term + 2, ..claim2 }), + (id3, Claim { term_max: max_term + 3, ..claim3 }), + ]; rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, Address::new_id(CLIENT1)); - let ret = h.extend_claim_terms(&rt, ¶ms).unwrap(); + let ret = h.extend_claim_terms(&rt, ¶ms, expected_claims).unwrap(); assert_eq!(ret.codes(), vec![ExitCode::OK, ExitCode::OK, ExitCode::OK]); - - // Verify state directly. - assert_claim(&rt, PROVIDER1, id1, &Claim { term_max: max_term + 1, ..claim1 }); - assert_claim(&rt, PROVIDER1, id2, &Claim { term_max: max_term + 2, ..claim2 }); - assert_claim(&rt, PROVIDER2, id3, &Claim { term_max: max_term + 3, ..claim3 }); h.check_state(&rt); } @@ -833,7 +885,7 @@ mod allocs_claims { terms: vec![ClaimTerm { provider: PROVIDER1, claim_id, term_max: max_term }], }; rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, Address::new_id(CLIENT1)); - let ret = h.extend_claim_terms(&rt, ¶ms).unwrap(); + let ret = h.extend_claim_terms(&rt, ¶ms, vec![(claim_id, claim.clone())]).unwrap(); assert_eq!(ret.codes(), vec![ExitCode::OK]); rt.verify() } @@ -844,7 +896,7 @@ mod allocs_claims { terms: vec![ClaimTerm { provider: PROVIDER1, claim_id, term_max: max_term }], }; rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, Address::new_id(CLIENT2)); - let ret = h.extend_claim_terms(&rt, ¶ms).unwrap(); + let ret = h.extend_claim_terms(&rt, ¶ms, vec![]).unwrap(); assert_eq!(ret.codes(), vec![ExitCode::USR_FORBIDDEN]); rt.verify() } @@ -855,7 +907,7 @@ mod allocs_claims { terms: vec![ClaimTerm { provider: PROVIDER2, claim_id, term_max: max_term }], }; rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, Address::new_id(CLIENT1)); - let ret = h.extend_claim_terms(&rt, ¶ms).unwrap(); + let ret = h.extend_claim_terms(&rt, ¶ms, vec![]).unwrap(); assert_eq!(ret.codes(), vec![ExitCode::USR_NOT_FOUND]); rt.verify() } @@ -870,7 +922,7 @@ mod allocs_claims { }], }; rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, Address::new_id(CLIENT1)); - let ret = h.extend_claim_terms(&rt, ¶ms).unwrap(); + let ret = h.extend_claim_terms(&rt, ¶ms, vec![]).unwrap(); assert_eq!(ret.codes(), vec![ExitCode::USR_ILLEGAL_ARGUMENT]); rt.verify() } @@ -881,7 +933,7 @@ mod allocs_claims { terms: vec![ClaimTerm { provider: PROVIDER1, claim_id, term_max: max_term - 1 }], }; rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, Address::new_id(CLIENT1)); - let ret = h.extend_claim_terms(&rt, ¶ms).unwrap(); + let ret = h.extend_claim_terms(&rt, ¶ms, vec![]).unwrap(); assert_eq!(ret.codes(), vec![ExitCode::USR_ILLEGAL_ARGUMENT]); rt.verify() } @@ -897,7 +949,13 @@ mod allocs_claims { }; rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, Address::new_id(CLIENT1)); rt.set_epoch(max_term + 1); - let ret = h.extend_claim_terms(&rt, ¶ms).unwrap(); + let ret = h + .extend_claim_terms( + &rt, + ¶ms, + vec![(claim_id, Claim { term_max: MAXIMUM_VERIFIED_ALLOCATION_TERM, ..claim })], + ) + .unwrap(); assert_eq!(ret.codes(), vec![ExitCode::OK]); rt.verify() } @@ -939,38 +997,41 @@ mod allocs_claims { let state_with_allocs: State = rt.get_state(); // Removal of expired claims shares most of its implementation with removing expired allocations. - // The full test suite is not duplicated here, simple ones to ensure that the expiration + // The full test suite is not duplicated here, simple ones to ensure that the expiration // is correctly computed. + let expect_1 = vec![(id1, claim1.clone())]; + let expect_2 = vec![(id2, claim2.clone())]; + let expect_both = vec![(id1, claim1), (id2, claim2)]; // None expired yet rt.set_epoch(term_start + term_min + 99); - let ret = h.remove_expired_claims(&rt, PROVIDER1, vec![id1, id2]).unwrap(); + let ret = h.remove_expired_claims(&rt, PROVIDER1, vec![id1, id2], vec![]).unwrap(); assert_eq!(vec![1, 2], ret.considered); assert_eq!(vec![ExitCode::USR_FORBIDDEN, ExitCode::USR_FORBIDDEN], ret.results.codes()); // One expired rt.set_epoch(term_start + term_min + 100); - let ret = h.remove_expired_claims(&rt, PROVIDER1, vec![id1, id2]).unwrap(); + let ret = h.remove_expired_claims(&rt, PROVIDER1, vec![id1, id2], expect_1).unwrap(); assert_eq!(vec![1, 2], ret.considered); assert_eq!(vec![ExitCode::OK, ExitCode::USR_FORBIDDEN], ret.results.codes()); // Both now expired rt.set_epoch(term_start + term_min + 200); - let ret = h.remove_expired_claims(&rt, PROVIDER1, vec![id1, id2]).unwrap(); + let ret = h.remove_expired_claims(&rt, PROVIDER1, vec![id1, id2], expect_2).unwrap(); assert_eq!(vec![1, 2], ret.considered); assert_eq!(vec![ExitCode::USR_NOT_FOUND, ExitCode::OK], ret.results.codes()); // Reset state, and show that specifying none removes only expired allocations rt.set_epoch(term_start + term_min); rt.replace_state(&state_with_allocs); - let ret = h.remove_expired_claims(&rt, PROVIDER1, vec![]).unwrap(); + let ret = h.remove_expired_claims(&rt, PROVIDER1, vec![], vec![]).unwrap(); assert_eq!(Vec::::new(), ret.considered); assert_eq!(Vec::::new(), ret.results.codes()); assert!(h.load_claim(&rt, PROVIDER1, id1).is_some()); assert!(h.load_claim(&rt, PROVIDER1, id2).is_some()); rt.set_epoch(term_start + term_min + 200); - let ret = h.remove_expired_claims(&rt, PROVIDER1, vec![]).unwrap(); + let ret = h.remove_expired_claims(&rt, PROVIDER1, vec![], expect_both).unwrap(); assert_eq!(vec![1, 2], ret.considered); assert_eq!(vec![ExitCode::OK, ExitCode::OK], ret.results.codes()); assert!(h.load_claim(&rt, PROVIDER1, id1).is_none()); // removed @@ -981,34 +1042,21 @@ mod allocs_claims { #[test] fn claims_restricted_correctly() { let (h, rt) = new_harness(); - let size = MINIMUM_VERIFIED_ALLOCATION_SIZE as u64; - let sector = 0; - let start = 0; - let min_term = MINIMUM_VERIFIED_ALLOCATION_TERM; - let max_term = min_term + 1000; - - let claim1 = make_claim("1", CLIENT1, PROVIDER1, size, min_term, max_term, start, sector); - - let id1 = h.create_claim(&rt, &claim1).unwrap(); - - // First, let's extend some claims - - let params = ExtendClaimTermsParams { - terms: vec![ClaimTerm { provider: PROVIDER1, claim_id: id1, term_max: max_term + 1 }], - }; - // set caller to not-builtin + // Extend claims. + // Empty request to avoid setting expectations for events etc. + let params = ExtendClaimTermsParams { terms: vec![] }; + // Set caller to not-builtin rt.set_caller(*EVM_ACTOR_CODE_ID, Address::new_id(CLIENT1)); - - // cannot call the unexported extend method num + // Cannot call the unexported method. expect_abort_contains_message( ExitCode::USR_FORBIDDEN, "must be built-in", - h.extend_claim_terms(&rt, ¶ms), + h.extend_claim_terms(&rt, ¶ms, vec![]), ); + rt.reset(); - // can call the exported method num - + // Can call the exported method. rt.expect_validate_caller_any(); let ret: ExtendClaimTermsReturn = rt .call::( @@ -1019,44 +1067,28 @@ mod allocs_claims { .unwrap() .deserialize() .expect("failed to deserialize extend claim terms return"); - rt.verify(); + assert_eq!(ret.codes(), vec![]); - assert_eq!(ret.codes(), vec![ExitCode::OK]); - - // Now let's Get those Claims, and check them - - let params = GetClaimsParams { claim_ids: vec![id1], provider: PROVIDER1 }; - - // cannot call the unexported extend method num + // Get claims. + let params = GetClaimsParams { claim_ids: vec![], provider: PROVIDER1 }; + // Cannot call the unexported method. expect_abort_contains_message( ExitCode::USR_FORBIDDEN, "must be built-in", - rt.call::( - Method::GetClaims as MethodNum, - IpldBlock::serialize_cbor(¶ms).unwrap(), - ), + h.get_claims(&rt, PROVIDER1, vec![]), ); + rt.reset(); - rt.verify(); - - // can call the exported method num + // Can call the exported method. rt.expect_validate_caller_any(); - let ret: GetClaimsReturn = rt - .call::( - Method::GetClaimsExported as MethodNum, - IpldBlock::serialize_cbor(¶ms).unwrap(), - ) - .unwrap() - .unwrap() - .deserialize() - .expect("failed to deserialize get claims return"); - + rt.call::( + Method::GetClaimsExported as MethodNum, + IpldBlock::serialize_cbor(¶ms).unwrap(), + ) + .unwrap() + .unwrap(); rt.verify(); - - assert_eq!(ret.batch_info.codes(), vec![ExitCode::OK]); - assert_eq!(ret.claims, vec![Claim { term_max: max_term + 1, ..claim1 }]); - h.check_state(&rt); } } @@ -1064,6 +1096,7 @@ mod allocs_claims { mod datacap { use frc46_token::receiver::FRC46_TOKEN_TYPE; use fvm_actor_utils::receiver::UniversalReceiverParams; + use fvm_ipld_encoding::ipld_block::IpldBlock; use fvm_shared::address::Address; use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; @@ -1079,7 +1112,6 @@ mod datacap { use fil_actors_runtime::{ BatchReturn, DATACAP_TOKEN_ACTOR_ADDR, EPOCHS_IN_YEAR, STORAGE_MARKET_ACTOR_ADDR, }; - use fvm_ipld_encoding::ipld_block::IpldBlock; use harness::*; use crate::*; @@ -1285,6 +1317,7 @@ mod datacap { .as_str(), h.receive_tokens(&rt, payload, BatchReturn::ok(1), BATCH_EMPTY, vec![1], 0), ); + rt.reset(); h.check_state(&rt); } @@ -1404,11 +1437,13 @@ mod datacap { "term_max 5260486 for claim 1 exceeds maximum 5260485 at current epoch 1100", h.receive_tokens(&rt, payload, BATCH_EMPTY, BATCH_EMPTY, vec![], 0), ); + rt.reset(); // But just on the limit is allowed let reqs = vec![make_extension_req(PROVIDER1, cid1, max_allowed_term)]; let payload = make_receiver_hook_token_payload(CLIENT1, vec![], reqs, SIZE); h.receive_tokens(&rt, payload, BATCH_EMPTY, BatchReturn::ok(1), vec![], SIZE).unwrap(); h.check_state(&rt); + rt.reset(); } { // Claim already expired @@ -1423,6 +1458,7 @@ mod datacap { "claim 1 expired at 518600, current epoch 518601", h.receive_tokens(&rt, payload, BATCH_EMPTY, BATCH_EMPTY, vec![], 0), ); + rt.reset(); // But just at expiration is allowed let epoch = term_start + term_max; let new_term = epoch - term_start + MAXIMUM_VERIFIED_ALLOCATION_TERM; // Can get full max term now @@ -1431,6 +1467,7 @@ mod datacap { let payload = make_receiver_hook_token_payload(CLIENT1, vec![], reqs, SIZE); h.receive_tokens(&rt, payload, BATCH_EMPTY, BatchReturn::ok(1), vec![], SIZE).unwrap(); h.check_state(&rt); + rt.reset(); } { // Extension is zero @@ -1443,6 +1480,7 @@ mod datacap { "term_max 518500 for claim 1 is not larger than existing term max 518500", h.receive_tokens(&rt, payload, BATCH_EMPTY, BATCH_EMPTY, vec![], 0), ); + rt.reset(); // Extension is negative let reqs = vec![make_extension_req(PROVIDER1, cid1, term_max - 1)]; let payload = make_receiver_hook_token_payload(CLIENT1, vec![], reqs, SIZE); @@ -1451,11 +1489,13 @@ mod datacap { "term_max 518499 for claim 1 is not larger than existing term max 518500", h.receive_tokens(&rt, payload, BATCH_EMPTY, BATCH_EMPTY, vec![], 0), ); + rt.reset(); // But extension by just 1 epoch is allowed let reqs = vec![make_extension_req(PROVIDER1, cid1, term_max + 1)]; let payload = make_receiver_hook_token_payload(CLIENT1, vec![], reqs, SIZE); h.receive_tokens(&rt, payload, BATCH_EMPTY, BatchReturn::ok(1), vec![], SIZE).unwrap(); h.check_state(&rt); + rt.reset(); } } } diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index a9d17d3e0f..1baf24cb6b 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -1273,10 +1273,8 @@ impl Runtime for MockRuntime { .borrow_mut() .expect_emitted_events .pop_front() - .expect("unexpected call to emit_evit"); - + .expect("unexpected call to emit_event"); assert_eq!(*event, expected); - Ok(()) } diff --git a/runtime/src/util/events.rs b/runtime/src/util/events.rs new file mode 100644 index 0000000000..00ee29639b --- /dev/null +++ b/runtime/src/util/events.rs @@ -0,0 +1,128 @@ +use crate::cbor::serialize_vec; +use crate::ActorError; +use fvm_shared::event::{ActorEvent, Entry, Flags}; +use serde::ser; + +// Codec identifier for CBOR-encoded data. +const IPLD_CBOR: u64 = 0x51; + +/// Builder for ActorEvent objects, accumulating key/value pairs. +pub struct EventBuilder { + entries: Vec, +} + +impl EventBuilder { + /// Creates a new builder with no values. + pub fn new() -> Self { + Self { entries: Vec::new() } + } + + /// Pushes an entry with an indexed key and no value. + pub fn label(mut self, name: &str) -> Self { + self.entries.push(Entry { + flags: Flags::FLAG_INDEXED_KEY, + key: name.to_string(), + codec: 0, + value: vec![], + }); + self + } + + /// Pushes an entry with an indexed key and an un-indexed, IPLD-CBOR-serialized value. + pub fn value( + mut self, + name: &str, + value: &T, + ) -> Result { + self.entries.push(Entry { + flags: Flags::FLAG_INDEXED_KEY, + key: name.to_string(), + codec: IPLD_CBOR, + value: serialize_vec(&value, "event value")?, + }); + Ok(self) + } + + /// Pushes an entry with an indexed key and indexed, IPLD-CBOR-serialized value. + pub fn value_indexed( + mut self, + name: &str, + value: &T, + ) -> Result { + self.entries.push(Entry { + flags: Flags::FLAG_INDEXED_ALL, + key: name.to_string(), + codec: IPLD_CBOR, + value: serialize_vec(&value, "event value")?, + }); + Ok(self) + } + + /// Returns an actor event ready to emit (consuming self). + pub fn build(self) -> ActorEvent { + ActorEvent { entries: self.entries } + } +} + +impl Default for EventBuilder { + fn default() -> Self { + Self::new() + } +} + +#[cfg(test)] +mod test { + use crate::{ActorError, EventBuilder}; + use fvm_shared::event::{ActorEvent, Entry, Flags}; + use crate::util::events::IPLD_CBOR; + + #[test] + fn label() { + let e = EventBuilder::new().label("l1").label("l2").build(); + assert_eq!( + ActorEvent { + entries: vec![ + Entry { + flags: Flags::FLAG_INDEXED_KEY, + key: "l1".to_string(), + codec: 0, + value: vec![], + }, + Entry { + flags: Flags::FLAG_INDEXED_KEY, + key: "l2".to_string(), + codec: 0, + value: vec![], + }, + ] + }, + e + ) + } + + #[test] + fn values() -> Result<(), ActorError> { + let e = EventBuilder::new().value("v1", &3)?.value_indexed("v2", "abc")?.build(); + assert_eq!( + ActorEvent { + entries: vec![ + Entry { + flags: Flags::FLAG_INDEXED_KEY, + key: "v1".to_string(), + codec: IPLD_CBOR, + value: vec![0x03], + }, + Entry { + flags: Flags::FLAG_INDEXED_ALL, + key: "v2".to_string(), + codec: IPLD_CBOR, + value: vec![0x63, 0x61, 0x62, 0x63], // CBOR for "abc" + }, + ] + }, + e + ); + + Ok(()) + } +} diff --git a/runtime/src/util/mod.rs b/runtime/src/util/mod.rs index d2dbd22fe3..dbf54b1db6 100644 --- a/runtime/src/util/mod.rs +++ b/runtime/src/util/mod.rs @@ -5,6 +5,7 @@ pub use self::batch_return::BatchReturn; pub use self::batch_return::BatchReturnGen; pub use self::batch_return::FailCode; pub use self::downcast::*; +pub use self::events::*; pub use self::mapmap::MapMap; pub use self::message_accumulator::MessageAccumulator; pub use self::multimap::*; @@ -14,6 +15,7 @@ pub use self::set_multimap::SetMultimap; mod batch_return; pub mod cbor; mod downcast; +mod events; mod mapmap; mod message_accumulator; mod multimap;