Logo

    Data cap token design

    Creator
    Alex North
    Created
    Jul 15, 2022 7:18 AM
    Stage
    Still Valid

    This is an iteration on part of ➕FIL+ indefinite term limits, exploring the design of FIL+ data cap as a fungible token.

    💡
    Work in progress

    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.

    Data cap fungible token actor

    This token is in whole units only (representing bytes).

    Data cap claim non-fungible token actor

    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.

    CryptoNet is a Protocol Labs initiative.

    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: ...}) -> {...}
    
    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.
    
    
    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.