This is an iteration on part of FIL+ indefinite term limits, exploring the design of FIL+ data cap as a fungible token.
The verified registry actor will perform multiple significant tasks:
- Account for the verifiers (notaries) and verified clients data cap allowances
- Track the allocations of data cap to specified pieces
- Track the claims of data cap by sectors
These need to be broken into three separate actors.
Verified Registry actor
This actor controls minting of the data cap token and tracks allocations of data cap to specific pieces (we could maybe even break out these claims to a new actor).
Verifiers have ability to mint tokens to clients. The verifiers’ minting allowance isn’t represented as a token.
When data cap is allocated, the registry transfers data cap balance to its own account. Its balance represents allocated but not yet claimed data cap.
When an allocation is claimed, the registry burns the associated data cap tokens.
type StoragePower = BigInt // bytes
type Allocation struct {
Client Address // Could drop this if it's in the map key
Provider Address // Optional
Piece CID
Size uint64
TermMinimum uint64
TermMaximum uint64
Expiration Epoch
AllOrNothing bool // Optional? Require atomic commit of entire piece
}
State {
root_key: Address,
// HAMT[Address]StoragePower
// Maps verifiers to their minting allowance.
verifiers: Cid,
remove_data_cap_proposal_ids: Cid,
// Allocations by client, then by ID.
allocations: HAMT[Address]AMT[AllocID]Allocation
next_allocation_id: uint64
// TODO: verifier delegates (like markets)
}
// Add minting allowance to a verifier.
// Callable only by root key.
fn add_verifier({address: Address, allowance: StoragePower})
fn remove_verifier(address: Address)
// Mints tokens to an address and decreases caller's minting allowance.
// Callable by verifier.
fn add_verified_client({address: Address, allowance: StoragePower})
// Burns tokens.
// Callable by root.
fn remove_verified_data_cap({...})
// Called by a verified client, or a delegate, to allocate some of its
// data cap balance to specific pieces and providers.
// Creates an allocation record and transfers the appropriate data cap
// token to this registry's balance.
// Replaces use_bytes.
fn allocate_data_cap({allocations: []DataCapAllocation})
// Cleans up all expired allocations for client.
// Data cap for removed allocations is transferred back to client's balance.
// Note that un-expired allocations cannot be revoked.
// Replaces restore_bytes.
fn revoke_allocations({client: Address})
// Called by storage miner actor to claim allocations for data
// provably committed to storage.
// Burns the associated data cap tokens from this actor's balance.
fn claim_allocations({sectors: ...}) -> {...}
Data cap fungible token actor
This token is in whole units only (representing bytes).
State {
// HAMT[Address]BigInt
// This replaces the old verifreg.verified_clients map
balances: Cid
}
// Mints new data cap to a verified client.
// Callable only by the verified registry.
fn mint({address: Address, amount: TokenAmount})
// Burns data cap of the caller
// (Standard token method)
fn burn({amount: TokenAmount})
// Burns data cap of a verified client.
// Callable only by the verified registry.
// (Standard token method)
fn burn_from({address: Address, amount: TokenAmount})
// Transfers tokens between addresses.
// Callable only by the verified registry
// (Standard token method)
fn transfer_from({from: Address, to: Address, amount: TokenAmount})
// Other standard token read-only methods as usual.
// Other standard token mutation methods all abort.
Data cap claim non-fungible token actor
type Claim struct {
Provider Address // Could drop this if it's in map key
Client Address
Piece CID
Size uint64
// This representation is for super-sector allocations.
// It's unfortunately not compressible while retaining the association
// between specific sectors and their range of the piece.
// Non-overlapping ranges. Store sorted by RangeStart? Use an AMT?
Sectors [] {
Sector SectorID,
RangeStart uint64,
RangeEnd uint64,
}
TermStart ChainEpoch // Epoch when piece was committed
TermMinimum ChainEpoch
TermMaximum ChainEpoch
}
State {
// HAMT[Address]AMT[ClaimID]Claim
// Claims by provider ID, then Claim ID.
// Claim ID is inherited from allocation ID.
claims: Cid
// Number of current claims
claim_count: uint64
}
// Mints a claim NFT.
// Callable only by verified registry.
fn mint({claim data...}) -> uint64
fn remove_expired_claims(...)
// TODO more
// Other standard nft read-only methods as usual.
// Other standard nft mutation methods all abort.
Design notes
- The existing methods use a BigInt representing storage power bytes. Token APIs may expect a TokenAmount (18 dp) value. Internal state can use the byte values, but there’s possibility for confusion.