Skip to content

Commit

Permalink
WIP iteration for allocs/claims
Browse files Browse the repository at this point in the history
  • Loading branch information
anorth committed Oct 27, 2023
1 parent b2721f8 commit 9b467f3
Show file tree
Hide file tree
Showing 10 changed files with 259 additions and 22 deletions.
45 changes: 33 additions & 12 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ fvm_sdk = "4.0.0-alpha.4"
fvm_shared = "4.0.0-alpha.4"
fvm_ipld_encoding = "0.4.0"
fvm_ipld_blockstore = "0.2.0"
fvm_ipld_hamt = "0.8.0"
fvm_ipld_hamt = "0.9.0"
fvm_ipld_kamt = "0.3.0"
fvm_ipld_amt = { version = "0.6.2" }
fvm_ipld_bitfield = "0.6.0"
Expand Down
1 change: 1 addition & 0 deletions actors/verifreg/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ cid = { workspace = true }
frc42_dispatch = { workspace = true }
frc46_token = { workspace = true }
fvm_actor_utils = { workspace = true }
fvm_ipld_bitfield = { workspace = true }
fvm_ipld_blockstore = { workspace = true }
fvm_ipld_encoding = { workspace = true }
fvm_ipld_hamt = { workspace = true }
Expand Down
3 changes: 1 addition & 2 deletions actors/verifreg/src/expiration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@ where
collection
.for_each_in(owner, |key, record| {
if curr_epoch >= record.expiration() {
let id = parse_uint_key(key)
.context_code(ExitCode::USR_ILLEGAL_STATE, "failed to parse uint key")?;
let id = parse_uint_key(key)?;
found_ids.push(id);
}
Ok(())
Expand Down
29 changes: 28 additions & 1 deletion actors/verifreg/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ use fil_actors_runtime::{ActorContext, AsActorError, BatchReturnGen};

use crate::ext::datacap::{DestroyParams, MintParams};
use crate::state::{
DataCapMap, RemoveDataCapProposalMap, DATACAP_MAP_CONFIG, REMOVE_DATACAP_PROPOSALS_CONFIG,
Cursor, DataCapMap, RemoveDataCapProposalMap, DATACAP_MAP_CONFIG,
REMOVE_DATACAP_PROPOSALS_CONFIG,
};

pub use self::state::Allocation;
Expand Down Expand Up @@ -70,6 +71,8 @@ pub enum Method {
GetClaimsExported = frc42_dispatch::method_hash!("GetClaims"),
ExtendClaimTermsExported = frc42_dispatch::method_hash!("ExtendClaimTerms"),
RemoveExpiredClaimsExported = frc42_dispatch::method_hash!("RemoveExpiredClaims"),
ListAllocationsExported = frc42_dispatch::method_hash!("ListAllocations"),
ListClaimsExported = frc42_dispatch::method_hash!("ListClaims"),
UniversalReceiverHook = frc42_dispatch::method_hash!("Receive"),
}

Expand Down Expand Up @@ -604,6 +607,28 @@ impl Actor {
Ok(RemoveExpiredClaimsReturn { considered, results: batch_ret })
}

pub fn list_allocations(
rt: &impl Runtime,
params: ListAllocationsParams,
) -> Result<ListAllocationsResponse, ActorError> {
let cursor = Cursor::from_bytes(params.cursor)?;
let st: State = rt.state()?;
let (allocations, next_cursor) = st.list_allocations(rt.store(), cursor, Some(params.limit))?;
let next_cursor = next_cursor.map(|c| c.to_bytes()).transpose()?.unwrap_or(RawBytes::default());
Ok(ListAllocationsResponse { allocations, next_cursor })
}

pub fn list_claims(
rt: &impl Runtime,
params: ListClaimsParams,
) -> Result<ListClaimsResponse, ActorError> {
let cursor = Cursor::from_bytes(params.cursor)?;
let st: State = rt.state()?;
let (claims, next_cursor) = st.list_claims(rt.store(), cursor, Some(params.limit))?;
let next_cursor = next_cursor.map(|c| c.to_bytes()).transpose()?.unwrap_or(RawBytes::default());
Ok(ListClaimsResponse { claims, next_cursor })
}

// 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 @@ -1069,6 +1094,8 @@ impl ActorCode for Actor {
GetClaims|GetClaimsExported => get_claims,
ExtendClaimTerms|ExtendClaimTermsExported => extend_claim_terms,
RemoveExpiredClaims|RemoveExpiredClaimsExported => remove_expired_claims,
ListAllocationsExported => list_allocations,
ListClaimsExported => list_claims,
UniversalReceiverHook => universal_receiver_hook,
}
}
105 changes: 103 additions & 2 deletions actors/verifreg/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

use cid::Cid;
use fvm_ipld_blockstore::Blockstore;
use fvm_ipld_encoding::RawBytes;
use fvm_ipld_encoding::tuple::*;
use fvm_shared::address::Address;
use fvm_shared::bigint::bigint_ser::BigIntDe;
Expand All @@ -13,10 +14,11 @@ use fvm_shared::sector::SectorNumber;
use fvm_shared::{ActorID, HAMT_BIT_WIDTH};

use fil_actors_runtime::{
actor_error, ActorError, AsActorError, Config, Map2, MapMap, DEFAULT_HAMT_CONFIG,
actor_error, parse_uint_key, ActorError, AsActorError, Config, Map2, MapMap,
DEFAULT_HAMT_CONFIG,
};

use crate::{AddrPairKey, AllocationID, ClaimID};
use crate::{AddrPairKey, AllocationID, AllocationKey, ClaimID, ClaimKey};
use crate::{DataCap, RemoveDataCapProposalID};

pub type DataCapMap<BS> = Map2<BS, Address, BigIntDe>;
Expand Down Expand Up @@ -156,6 +158,34 @@ impl State {
Ok(allocated_ids)
}

// Lists all allocation clients and IDs, paginated with a cursor.
// Returns a new cursor from which to continue listing, if any items remain.
pub fn list_allocations<BS: Blockstore>(
&self,
store: &BS,
cursor: Option<Cursor>,
limit: Option<u64>,
) -> Result<(Vec<AllocationKey>, Option<Cursor>), ActorError> {
let (start_outer, start_inner) = verify_cursor(&cursor, self.allocations)?;
let mut allocs = self.load_allocs(store)?;
let mut found = vec![];
let next = allocs
.for_each_each(start_outer, start_inner, limit, |k1, k2, _v| {
let client = parse_uint_key(k1)?;
let id = parse_uint_key(k2)?;
found.push(AllocationKey { client, id });
Ok(())
})
.context_code(ExitCode::USR_ILLEGAL_STATE, "listing allocations")?;
let next_cursor = match next {
Some((k1, k2)) => {
Some(Cursor::new(self.allocations, parse_uint_key(&k1)?, parse_uint_key(&k2)?))
}
None => None,
};
Ok((found, next_cursor))
}

pub fn load_claims<'a, BS: Blockstore>(
&self,
store: &'a BS,
Expand Down Expand Up @@ -196,7 +226,36 @@ impl State {
self.save_claims(&mut st_claims)?;
Ok(())
}

// Lists all claim providers and IDs, paginated with a cursor.
// Returns a new cursor from which to continue listing, if any items remain.
pub fn list_claims<BS: Blockstore>(
&self,
store: &BS,
cursor: Option<Cursor>,
limit: Option<u64>,
) -> Result<(Vec<ClaimKey>, Option<Cursor>), ActorError> {
let (start_outer, start_inner) = verify_cursor(&cursor, self.claims)?;
let mut claims = self.load_claims(store)?;
let mut found = vec![];
let next = claims
.for_each_each(start_outer, start_inner, limit, |k1, k2, _v| {
let provider = parse_uint_key(k1)?;
let id = parse_uint_key(k2)?;
found.push(ClaimKey { provider, id });
Ok(())
})
.context_code(ExitCode::USR_ILLEGAL_STATE, "listing claims")?;
let next_cursor = match next {
Some((k1, k2)) => {
Some(Cursor::new(self.claims, parse_uint_key(&k1)?, parse_uint_key(&k2)?))
}
None => None,
};
Ok((found, next_cursor))
}
}

#[derive(Serialize_tuple, Deserialize_tuple, Clone, Debug, PartialEq, Eq)]
pub struct Claim {
// The provider storing the data (from allocation).
Expand Down Expand Up @@ -262,3 +321,45 @@ where
.get(provider, id)
.context_code(ExitCode::USR_ILLEGAL_STATE, "HAMT lookup failure getting claim")
}

/// Opaque cursor to iterate over allocations/claims data structures
#[derive(Serialize_tuple, Deserialize_tuple, Clone, Debug)]
pub struct Cursor {
pub root: Cid,
pub outer_key: ActorID,
pub inner_key: u64,
}

impl Cursor {
pub fn new(cid: Cid, inner_key: ActorID, outer_key: u64) -> Self {
Self { root: cid, inner_key, outer_key }
}

/// Generates a cursor from an opaque representation
pub fn from_bytes(bytes: RawBytes) -> Result<Option<Cursor>, ActorError> {
if bytes.is_empty() {
Ok(None)
} else {
Ok(Some(fvm_ipld_encoding::from_slice(&bytes)?))
}
}

/// Generates an opaque representation of the cursor that can be used to resume enumeration
pub fn to_bytes(&self) -> Result<RawBytes, ActorError> {
Ok(RawBytes::from(fvm_ipld_encoding::to_vec(self)?))
}
}

fn verify_cursor(
cursor: &Option<Cursor>,
expected_root: Cid,
) -> Result<(Option<&ActorID>, Option<&u64>), ActorError> {
if let Some(cursor) = cursor {
if cursor.root != expected_root {
return Err(ActorError::illegal_argument("invalid cursor".to_string()));
// FIXME new error type
}
return Ok((Some(&cursor.outer_key), Some(&cursor.inner_key)))
}
Ok((None, None))
}
Loading

0 comments on commit 9b467f3

Please sign in to comment.