Architecture for programmable storage markets

Last edited
Jul 28, 2022 1:33 AM
Storage Market Programmability

This note sketches out a refactor of the interfaces and calls between the miner, market and FIL+ registry actors in order to support alternative storage markets.

The canonical version is now in FIPs 👇


  • Break any dependency of the miner actor on the market
  • Break coupling between the market and FIL+ actors
  • Don’t trust the market actor for anything except maintaining its own internal deal state
  • Thus allow an alternative storage market to compete with the built-in one, including for FIL+ deals
  • Retain the current economic structure, incentives, pledge etc
  • Be compatible with future change to

Supporting alternative markets also requires

. See also
Market/deal contract interfaces notes
for other programmability enhancements.

Questions to check if this is a good architecture

  • Could this work with an L2 market? How?
  • Could we even further remove knowledge of deals from the miner actor, so they don’t even pass through?
    • Would like to notify market of sector content async, after PoRep.
    • Challenge is associating piece CIDs with CommD when the CommD is proven to be correct, i.e. only at PoRep time
  • Could we get features of deal extension after this re-architecture, even while FIL+ remains? (Maybe with
    FIL+ Forever
    • Deals decoupled from FIL+, so non-verified deals are easy
    • Need extension of data cap
  • Could we add feature of upgrading a deal to FIL+ after it starts?
    • 2/3 of the way there, need a new method on miner to claim data cap after PoRep
    • And the challenge of proving piece inclusion in CommR again :-(

Design notes

  • We trust the miner actor as network code, so sensitive operations are moved into it.
  • Assumes QAP mechanism remains, so the FIL+ actor must be known and trusted by miner
    • Want to be on a path where FIL+ premium could be added later, with accounting in the FIL+ registry actor.
  • CommD <-> CommR correspondence is only give at PoRep/Snap time. It’s hard generally to prove the correspondence later.
  • There’s no enforcement that sector content must be zero outside deals.

Notable changes

  • FIL+ is actor informed about specific deal terms prior to deal
    • This can be extended into the FIL+ registry dishing out the FIL+ premium in the future
  • Miner actor invokes FIL+ to commit data cap, and gets the deal weight inputs for QAP in return
    • Previously invoked the market for this
  • Market no longer knows FIL+ or data cap at all
  • SP provides deal metadata at PoRep instead of fetching it from market at PreCommit
    • Miner computes CommD instead of calling market
    • Miner only invokes market once (today: thrice)


Miner actor


  • PreCommit state
    • SectorID, CommR, expiration etc
  • Sector state
    • Sector ID, CommR, expiration, pledge etc


  • PreCommitSector(SectorID, CommR, precommit stuff)
  • ProveCommitSector(SectorID, proof, CommD, []Pieces) Piece is (PieceCID, size, marketAddr?, dealID?, FIL+ ID?)
  • ProveReplicaUpdate(sectorID, proof, CommR, CommD, []Pieces)

Market actor


  • Deal proposals
  • Activated deal states
  • Mapping (Provider, Sector)→[deal ID]


  • PublishStorageDeal(...) → ID Or equivalent. Market-specific, does not need to be standardised.
  • SectorContentAdded(provider, sectorID, [(deal ID, pieceCID, size)]) Invoked by provider whenever sector content is added, passing through the list of deal/piece IDs specified by them. The miner actor verifies the piece data. With QAP/write-once storage, pieces are all new. Alternative semantics will be needed for update-able storage.
  • TODO: add ExtendDeal (or more generic renegotiation), plus tracking of deal sector expirations for automatic termination if not re-sealed

The on-chain DealProposal loses its VerifiedDeal field: the market is no longer trusted to determine verified status.

The off-chain StorageAsk struct gains two optional fields:

  • FilecoinPlusApprovalID : id of a previous data cap approval by the client
  • FilecoinPlusApprovalCertificate : a signed tuple of (pieceCID, size, start, end). The SP can submit this directly to the FIL+ actor’s ClaimAllocation when proving a sector.

FIL+ registry (without FIL+ premium)


  • Data cap total/used per client
  • Data cap approvals list by client


  • AllocateDataCap(client, provider, piece CID, size, term, label) → AllocID Called by client. Allocates client’s data cap for specific terms, prior to any deal. Consider allowing provider to be empty, so valid for any provider.
    • An alternative flow is for SP to provide a signed message from client during ClaimAllocation, saving client messages but costing a signature verification.
  • RevokeExpiredAllocations(client) Called by client. Removes inactive allocations with start epochs in the past, restoring the data cap for re-use. Removes allocations with end epochs in the past.
  • ClaimAllocation(provider, sector ID, claims [(AllocID, piece CID, size)], [pre-signed certificates]) → [allocations] Called by SP. Marks allocation as active & irrevocable if ID/piece/size match. Sector ID is for future tracking of sector faults etc. Returns the approvals activated.


End-to-end verified deal

  1. A client submits approval for specific deal terms to consume data cap to the FIL+ registry. This reserves a part the client’s data cap immediately. The registry allocates an ID for the registration and stores the terms under the registration ID for later reference.
    1. Alternatively, an EOA client can sign a message approving the data cap for the specific deal terms, which can be submitted to the FIL+ actor by the SP when the deal is committed.
    2. We need the client to commit to the data cap in order to induce the provider to take the deal.
  2. A market matches a deal client and provider with terms. The mechanism for this can vary between markets (e.g. on chain vs off-chain). The market and SP must know the deal client, size, term, and piece CID. The client and provider are both committed to the deal.
    1. This is PublishStorageDeals today.
  3. The SP pre-commits a sector, committing to CommR. The SP may have sealed deals into the sector, but they are not supplied on chain yet.
    1. The pre-commit deposit is changed from being equal to Initial Pledge, to being only the Storage Pledge part of IP for a fully-verified sector (i.e. 200 * BR). This is much less. No longer requires deal information at pre-commit.
  4. SP also provides a list of (piece CID, size, market addr, deal ID, AllocID) tuples for pieces sealed into sector. Pieces must exactly fill the sector (with an optional trailing zero piece consuming remaining space). A sector may have deals from multiple markets. A piece may have no deal or FIL+ data. Miner actor computes CommD from the piece CIDs The SP proves the sector, proving the CommD corresponds to CommR committed earlier. The miner actor claims each (AllocID, piece CID, size) as committed to the the FIL+ actor. The FIL+ actor checks these against client approvals, and returns the data cap’s space-time. The miner actor can then calculate quality-adjusted power given the sector’s expiration date. For each piece, the miner actor notifies the relevant market actor that the (deal ID, piece CID) has activated, and in which sector. The market checks the piece CID matches the deal, and records the sector→deal mapping.
    1. No deal information is preserved in miner sector state.
    2. Opportunity to allow deal lifespan to exceed sector lifespan, truncate QAP to the sector commitment.

If a sector is not proven after pre-commit

  • After the approval start epoch has passed, so it can’t be sealed into a new sector, client can revoke the FIL+ registration by garbage collecting. The market cleans up the deal in cron.

Old call sequence

  • SP → Market.PublishStorageDeals
  • SP → Miner.PreCommitSector[Batch]
    • Miner → Market.VerifyDealsForActivation
  • SP → Miner.ProveCommitSector[Aggregate]
    • Miner → Market.ComputeDataCommitment
    • Miner → Market.ActivateDeals

New call sequence

  • Client → VerifReg.AllocateDataCap
    • Contract clients need this, EOA clients can instead provide certificate along with storage ask
  • SP → Market.PublishStorageDeals
  • SP → Miner.PreCommitSector[Batch]
  • SP → Miner.ProveCommitSector[Aggregate]
    • Miner → VerifReg.ClaimAllocation
    • Miner → Market.SectorContentAdded (aka ActivateDeals)

Sector content update (Snap Deal)

  1. SP commits and proves a zero sector (no calls to market or FIL+).
  2. Client approves data cap, and provider/client publish a deal.
  3. SP invokes ProveReplicaUpdate, proving a new CommR. Includes a list of pieces that exactly fill the sector, with associated market deal ID and FIL+ ID. Miner computes CommD from pieces. As for the usual flow, for each piece the miner claims a FIL+ approval to calculate space-time, and notifies market that the piece has activated, and in which sector.

While the QAP mechanism exists, miner must enforce that only pieces previously zero can be updated.

Sector termination

Every miner is registered to notify the market actor of any early sector termination, via

. The miner no-longer knows which sectors have deals.

  1. Miner notifies market/s of sector termination, with sector IDs (and originally scheduled expiration epochs?)
  2. Market looks up its sector→deals mapping and marks any such deal IDs as terminated, but defers expensive processing for later.
    1. This avoids sector termination being too expensive to process
  3. During normal cron processing of active deals, market checks the terminated deal list, and processes any terminations.

Sector extension (optional)

We could allow deals to be committed into sectors that are currently set to expire too early, if we can extend them later.

  1. The market allows deals to be activated in sectors that have earlier expirations. The market records each deal’s sector number in state, and the expiration epoch of each relevant sector. If the sector is not extended in time, the deal is terminated during normal deal cron processing.
  2. SP invokes miner.ExtendSectorExpiration with a sector number and new expiration.
  3. Miner actor calls a new market.SectorExpirationChanged method with each registered market actor. The market actor updates the local sector expiration epoch cache.

Deal extension into new sector (optional, new!)

We could allow deals to be committed into sectors that are currently set to expire too early, if they can be moved into a new sector.

  1. The end-to-end deal flow happens, with a deal with a loooong expiration
    1. Or deal term is updated later
  2. Later, the miner proves the same deal in a new sector, providing the same (pieceCID, size, market, dealId, FIL+ ID) to PoRep, plus the existing sector ID. The miner actor claims the same (FIL+ ID, piece CID, size) from the FIL+ actor. If the approval term has not expired, the claim is allowed and FIL+ returns the sector ID that already hosts the piece, which the miner checks. The miner actor is trusted not the activate the QA power twice concurrently. Instead, the new sector’s QAP is calculated as if the deal only activated the epoch after the existing sector expires. The old sector maintains its power until it expires. The miner actor notifies the market actor that the same (deal ID, piece CID) has been committed in a new sector. The market forgets about the original sector and updates the sector→deals mapping to the new sector.

Deal extension with existing sector (optional)



We could allow deals to be extended if sector lifetime allows for it.

QA power in its current form is a blocker as QA power of a sector is write once mechanism.

  1. Possible notion: FIL+ Forever the time component of QA power is essentially skipped
  2. Possible option: No QA power from extension ← BAD
  3. Possible option: FIL+ premium


  1. The market recieves a message about a deal extension co-signed by both parties (either message + deal signature or dual message).
  2. Market calls into the miner checking the sector lifetime is sufficient for the extension (this step can be skipped if we implement “Sector extension”.
  3. Depending on FIL+ mechanism:
    1. FIL+ Forever - the state of the sector does not change
    2. No QA power from extension - the Quality of a sector is not adjusted so in essence miner does not gain QA power from the sector (the QA power of a sector was possibly earlier reduced due to sector extension).
    3. Change to FIL+ premium is sent
  4. The deal can be either updated in place or new deal can be created.


  • Deal extension within the same sector already storing it