Skip to content

Commit

Permalink
Implement verified claim term extension by client
Browse files Browse the repository at this point in the history
  • Loading branch information
anorth committed Sep 18, 2022
1 parent 16b923a commit 9594cf4
Show file tree
Hide file tree
Showing 7 changed files with 324 additions and 32 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions actors/verifreg/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ fvm_ipld_blockstore = "0.1.1"
fvm_ipld_encoding = "0.2.2"
fvm_ipld_hamt = "0.5.1"
fvm_shared = { version = "2.0.0-alpha.2", default-features = false }
itertools = "0.10.3"
lazy_static = "1.4.0"
log = "0.4.14"
num-derive = "0.3.3"
Expand Down
112 changes: 95 additions & 17 deletions actors/verifreg/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@ use fvm_shared::clock::ChainEpoch;
use fvm_shared::econ::TokenAmount;
use fvm_shared::error::ExitCode;
use fvm_shared::{ActorID, MethodNum, HAMT_BIT_WIDTH, METHOD_CONSTRUCTOR};
use itertools::Itertools;
use log::info;
use num_derive::FromPrimitive;
use num_traits::{FromPrimitive, Signed, Zero};

use crate::ext::datacap::{DestroyParams, MintParams};
use fil_actors_runtime::cbor::{deserialize, serialize};
use fil_actors_runtime::runtime::builtins::Type;
use fil_actors_runtime::runtime::{ActorCode, Policy, Runtime};
Expand All @@ -30,8 +32,6 @@ use fil_actors_runtime::{
};
use fil_actors_runtime::{ActorContext, AsActorError, BatchReturnGen, DATACAP_TOKEN_ACTOR_ADDR};

use crate::ext::datacap::{DestroyParams, MintParams};

pub use self::state::Allocation;
pub use self::state::Claim;
pub use self::state::State;
Expand Down Expand Up @@ -59,6 +59,7 @@ pub enum Method {
RemoveExpiredAllocations = 8,
ClaimAllocations = 9,
GetClaims = 10,
ExtendClaimTerms = 11,
UniversalReceiverHook = UNIVERSAL_RECEIVER_HOOK_METHOD_NUM,
}

Expand Down Expand Up @@ -350,10 +351,7 @@ impl Actor {
rt.transaction(|st: &mut State, rt| {
let mut allocs = st.load_allocs(rt.store())?;
for alloc_id in params.allocation_ids {
let maybe_alloc = allocs.get(client, alloc_id).context_code(
ExitCode::USR_ILLEGAL_STATE,
"HAMT lookup failure getting allocation",
)?;
let maybe_alloc = state::get_allocation(&mut allocs, client, alloc_id)?;
let alloc = match maybe_alloc {
None => {
ret_gen.add_fail(ExitCode::USR_NOT_FOUND);
Expand Down Expand Up @@ -417,12 +415,11 @@ impl Actor {
let mut allocs = st.load_allocs(rt.store())?;

for claim_alloc in params.sectors {
let maybe_alloc =
allocs.get(claim_alloc.client, claim_alloc.allocation_id).context_code(
ExitCode::USR_ILLEGAL_STATE,
"HAMT lookup failure getting allocation",
)?;

let maybe_alloc = state::get_allocation(
&mut allocs,
claim_alloc.client,
claim_alloc.allocation_id,
)?;
let alloc: &Allocation = match maybe_alloc {
None => {
ret_gen.add_fail(ExitCode::USR_NOT_FOUND);
Expand Down Expand Up @@ -506,11 +503,7 @@ impl Actor {
let mut st_claims = st.load_claims(rt.store())?;
let mut ret_claims = Vec::new();
for id in params.claim_ids {
let maybe_claim = st_claims.get(params.provider, id).context_code(
ExitCode::USR_ILLEGAL_STATE,
"HAMT lookup failure getting allocation",
)?;

let maybe_claim = state::get_claim(&mut st_claims, params.provider, id)?;
match maybe_claim {
None => {
batch_gen.add_fail(ExitCode::USR_NOT_FOUND);
Expand All @@ -528,6 +521,87 @@ impl Actor {
Ok(GetClaimsReturn { batch_info: batch_gen.gen(), claims })
}

/// Extends the maximum term of some claims up to the largest value they could have been
/// originally allocated.
/// Callable only by the claims' client.
/// Cannot reduce a claim's term.
/// Can extend the term even if the claim has already expired.
/// Note that this method can't extend the term past the original limit,
/// even if the term has previously been extended past that by spending new datacap.
pub fn extend_claim_terms<BS, RT>(
rt: &mut RT,
params: ExtendClaimTermsParams,
) -> Result<ExtendClaimTermsReturn, ActorError>
where
BS: Blockstore,
RT: Runtime<BS>,
{
// Permissions are checked per-claim.
rt.validate_immediate_caller_accept_any()?;
let caller_id = rt.message().caller().id().unwrap();
let term_limit = rt.policy().maximum_verified_allocation_term;

let mut batch_gen = BatchReturnGen::new(params.terms.len());
rt.transaction(|st: &mut State, rt| {
let mut st_claims = st.load_claims(rt.store())?;
// Group consecutive term extensions with the same provider so we can batch update
// the MapMap with new entries.
// This does not re-order the parameters, so that the batch return indices match.
// The caller can thus minimise gas consumption by grouping extensions by provider.
for (provider, terms) in &params.terms.iter().group_by(|e| e.provider) {
let mut provider_new_claims = Vec::<(ClaimID, Claim)>::new();
for term in terms {
// Confirm the new term limit is allowed.
if term.term_max > term_limit {
batch_gen.add_fail(ExitCode::USR_ILLEGAL_ARGUMENT);
info!(
"term_max {} for claim {} exceeds maximum {}",
term.term_max, term.claim_id, term_limit,
);
continue;
}

let maybe_claim = state::get_claim(&mut st_claims, provider, term.claim_id)?;
if let Some(claim) = maybe_claim {
// Confirm the caller is the claim's client.
if claim.client != caller_id {
batch_gen.add_fail(ExitCode::USR_FORBIDDEN);
info!(
"client {} for claim {} does not match caller {}",
claim.client, term.claim_id, caller_id,
);
continue;
}
// Confirm the new term limit is no less than the old one.
if term.term_max < claim.term_max {
batch_gen.add_fail(ExitCode::USR_ILLEGAL_ARGUMENT);
info!(
"term_max {} for claim {} is less than current {}",
term.term_max, term.claim_id, claim.term_max,
);
continue;
}

let new_claim = Claim { term_max: term.term_max, ..*claim };
provider_new_claims.push((term.claim_id, new_claim));
batch_gen.add_success();
} else {
batch_gen.add_fail(ExitCode::USR_NOT_FOUND);
info!("no claim {} for provider {}", term.claim_id, provider);
}
}
st_claims.put_many(provider, provider_new_claims.into_iter()).context_code(
ExitCode::USR_ILLEGAL_STATE,
"HAMT put failure storing new claims",
)?;
}
st.save_claims(&mut st_claims)?;
Ok(())
})
.context("state transaction failed")?;
Ok(batch_gen.gen())
}

// Receives data cap tokens (only) and creates allocations according to one or more
// allocation requests specified in the transfer's operator data.
// The token amount received must exactly correspond to the sum of the requested allocation sizes.
Expand Down Expand Up @@ -980,6 +1054,10 @@ impl ActorCode for Actor {
let res = Self::claim_allocations(rt, cbor::deserialize_params(params)?)?;
Ok(RawBytes::serialize(res)?)
}
Some(Method::ExtendClaimTerms) => {
let res = Self::extend_claim_terms(rt, cbor::deserialize_params(params)?)?;
Ok(RawBytes::serialize(res)?)
}
Some(Method::UniversalReceiverHook) => {
let res = Self::universal_receiver_hook(rt, cbor::deserialize_params(params)?)?;
Ok(RawBytes::serialize(res)?)
Expand Down
26 changes: 26 additions & 0 deletions actors/verifreg/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,3 +240,29 @@ pub struct Allocation {
}

impl Cbor for State {}

pub fn get_allocation<'a, BS>(
allocations: &'a mut MapMap<BS, Allocation, ActorID, AllocationID>,
client: ActorID,
id: AllocationID,
) -> Result<Option<&'a Allocation>, ActorError>
where
BS: Blockstore,
{
allocations
.get(client, id)
.context_code(ExitCode::USR_ILLEGAL_STATE, "HAMT lookup failure getting allocation")
}

pub fn get_claim<'a, BS>(
claims: &'a mut MapMap<BS, Claim, ActorID, ClaimID>,
provider: ActorID,
id: ClaimID,
) -> Result<Option<&'a Claim>, ActorError>
where
BS: Blockstore,
{
claims
.get(provider, id)
.context_code(ExitCode::USR_ILLEGAL_STATE, "HAMT lookup failure getting claim")
}
8 changes: 4 additions & 4 deletions actors/verifreg/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,15 +125,15 @@ pub type ClaimAllocationsReturn = BatchReturn;

#[derive(Clone, Debug, PartialEq, Eq, Serialize_tuple, Deserialize_tuple)]
pub struct ClaimTerm {
provider: ActorID,
claim_id: ClaimID,
term_max: ChainEpoch,
pub provider: ActorID,
pub claim_id: ClaimID,
pub term_max: ChainEpoch,
}
impl Cbor for ClaimTerm {}

#[derive(Clone, Debug, PartialEq, Eq, Serialize_tuple, Deserialize_tuple)]
pub struct ExtendClaimTermsParams {
pub claims: Vec<ClaimTerm>,
pub terms: Vec<ClaimTerm>,
}
impl Cbor for ExtendClaimTermsParams {}

Expand Down
70 changes: 60 additions & 10 deletions actors/verifreg/tests/harness/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ use num_traits::{ToPrimitive, Zero};
use fil_actor_verifreg::testing::check_state_invariants;
use fil_actor_verifreg::{
ext, Actor as VerifregActor, AddVerifierClientParams, AddVerifierParams, Allocation,
AllocationID, AllocationRequest, AllocationRequests, AllocationsResponse,
ClaimAllocationsParams, ClaimAllocationsReturn, ClaimID, DataCap, GetClaimsParams,
GetClaimsReturn, Method, RemoveExpiredAllocationsParams, RemoveExpiredAllocationsReturn,
SectorAllocationClaim, State,
AllocationID, AllocationRequest, AllocationRequests, AllocationsResponse, Claim,
ClaimAllocationsParams, ClaimAllocationsReturn, ClaimID, DataCap, ExtendClaimTermsParams,
ExtendClaimTermsReturn, GetClaimsParams, GetClaimsReturn, Method,
RemoveExpiredAllocationsParams, RemoveExpiredAllocationsReturn, SectorAllocationClaim, State,
};
use fil_actors_runtime::cbor::serialize;
use fil_actors_runtime::runtime::policy_constants::{
Expand All @@ -30,8 +30,8 @@ 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, MapMap, DATACAP_TOKEN_ACTOR_ADDR,
STORAGE_MARKET_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, VERIFIED_REGISTRY_ACTOR_ADDR,
make_empty_map, ActorError, AsActorError, DATACAP_TOKEN_ACTOR_ADDR, STORAGE_MARKET_ACTOR_ADDR,
SYSTEM_ACTOR_ADDR, VERIFIED_REGISTRY_ACTOR_ADDR,
};

lazy_static! {
Expand Down Expand Up @@ -205,12 +205,9 @@ impl Harness {
acc.assert_empty();
}

// TODO this should be implemented through a call to verifreg but for now it modifies state directly
pub fn create_alloc(&self, rt: &mut MockRuntime, alloc: &Allocation) -> Result<(), ActorError> {
let mut st: State = rt.get_state();
let mut allocs =
MapMap::from_root(rt.store(), &st.allocations, HAMT_BIT_WIDTH, HAMT_BIT_WIDTH)
.context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load allocations table")?;
let mut allocs = st.load_allocs(rt.store()).unwrap();
assert!(allocs
.put_if_absent(alloc.client, st.next_allocation_id, alloc.clone())
.context_code(ExitCode::USR_ILLEGAL_STATE, "faild to put")?);
Expand Down Expand Up @@ -318,6 +315,19 @@ impl Harness {
Ok(())
}

pub fn create_claim(&self, rt: &mut MockRuntime, claim: &Claim) -> Result<ClaimID, ActorError> {
let mut st: State = rt.get_state();
let mut claims = st.load_claims(rt.store()).unwrap();
let id = st.next_allocation_id;
assert!(claims
.put_if_absent(claim.provider, id, claim.clone())
.context_code(ExitCode::USR_ILLEGAL_STATE, "faild to put")?);
st.next_allocation_id += 1;
st.claims = claims.flush().expect("failed flushing allocation table");
rt.replace_state(&st);
Ok(id)
}

pub fn get_claims(
&self,
rt: &mut MockRuntime,
Expand All @@ -336,6 +346,23 @@ impl Harness {
rt.verify();
Ok(ret)
}

pub fn extend_claim_terms(
&self,
rt: &mut MockRuntime,
params: &ExtendClaimTermsParams,
) -> Result<ExtendClaimTermsReturn, ActorError> {
rt.expect_validate_caller_any();
let ret = rt
.call::<VerifregActor>(
Method::ExtendClaimTerms as MethodNum,
&serialize(&params, "extend claim terms params").unwrap(),
)?
.deserialize()
.expect("failed to deserialize extend claim terms return");
rt.verify();
Ok(ret)
}
}

pub fn make_alloc(data_id: &str, client: &Address, provider: &Address, size: u64) -> Allocation {
Expand Down Expand Up @@ -391,6 +418,29 @@ pub fn make_claim_req(
}
}

#[allow(clippy::too_many_arguments)]
pub fn make_claim(
data_id: &str,
client: &Address,
provider: &Address,
size: u64,
term_min: i64,
term_max: i64,
term_start: i64,
sector: u64,
) -> Claim {
Claim {
provider: provider.id().unwrap(),
client: client.id().unwrap(),
data: make_piece_cid(data_id.as_bytes()),
size: PaddedPieceSize(size),
term_min,
term_max,
term_start,
sector,
}
}

pub fn make_receiver_hook_token_payload(
client: ActorID,
requests: Vec<AllocationRequest>,
Expand Down
Loading

0 comments on commit 9594cf4

Please sign in to comment.