Skip to content

About

No description, website, or topics provided.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Repository files navigation

PoRep Market

PoRep Market V2 coordinates Filecoin storage deals between clients and Storage Providers (SPs). It matches deal requests to provider offers, validates storage evidence, tracks provider capacity and deal state, and controls settlement through per-deal FilecoinPay validators.

The contracts, interfaces, and tests are authoritative. This README maintains only the system ownership and deal lifecycle needed to navigate them.

Deployments

V2

V1

main contains V2. V1 maintenance, releases, deployments, and upgrades belong on the separate v1 branch.

Architecture

  • PoRepMarket owns deal state, frozen deal terms, service dates, payment calculation, settlement progress, and lifecycle transitions. Its external contract is IPoRepMarket.
  • SPRegistry stores providers and their mutable offers, selects an offer for a request, and tracks available, reserved, and committed capacity.
  • DataCapEvidenceAdapter submits DataCap allocations, records Filecoin claims, and reports covered bytes. Evidence adapters implement IStorageEvidenceAdapter.
  • SectorEvidenceAdapter validates authenticated FIP-0109 placement callbacks against the complete piece-set commitment stored in a deal's manifestHash. It records each piece placement, unique sector numbers, covered bytes per sector, and the earliest accepted minimum commitment epoch. Activation requires that epoch to cover the activation block plus the complete deal duration. FIP-0112 refreshes run over the stored sector list.
  • Validator is the per-deal FilecoinPay operator. It controls the payment rail and delegates settlement decisions to PoRepMarket.
  • ValidatorFactory creates Validator beacon proxies for deals.
  • SLIOracle stores provider attestations, and SLIScorer evaluates the deal's frozen SLI thresholds during settlement.
  • FilecoinPay holds client funds, settles rails through Validator, and pays providers.

Canonical lifecycle codes and data structures live in DealState, PoRepTypes, and SharedTypes.

Why the adapter exists

DataCap and VerifReg are the current way PoRep Market checks storage, but this path is being phased out. V2 cannot bake DataCap allocations and claims into its deal and payment model and then require another market redesign when Filecoin removes them.

The adapter is the transition boundary. It translates whichever Filecoin storage mechanism is available into the few facts PoRep Market needs: how many deal bytes have storage coverage, whether the deal can activate, and whether that coverage is still current.

Today DataCapEvidenceAdapter gets those facts from DataCap allocations and VerifReg claims. A later adapter can use the post-DataCap mechanism without changing PoRepMarket, Validator, or the existing deal ABI.

Each deal keeps the adapter selected when it was created. New deals can move to the replacement while older deals remain tied to the path they started with. Fresh deployments include SectorEvidenceAdapter, but keep DataCapEvidenceAdapter as the global default. An administrator can select the sector adapter for new deals when running this PoC. No existing Calibnet or mainnet deployment is changed by this branch.

The sector adapter uses the same AccessManager and UPGRADER_ROLE as the other contracts. Deploy this rebased PoC fresh; existing PoC proxies initialized with adapter-local roles have no configured AccessManager. The first accepted piece locks the deal manifest, including before full activation.

The code calls these storage facts evidence. Evidence here means the on-chain information used to check storage coverage; it does not mean that every adapter returns a cryptographic proof. The rest of this README calls it the adapter.

Sector piece-set commitment v1

Before proposing a Sector evidence deal, the manifest builder rejects duplicate rows and sorts every data and DAG row by (PieceCID digest bytes32, paddedSize uint64). The zero-based index after sorting is part of the leaf. The tree is padded to the next power of two with a fixed empty leaf and uses ordered, not commutative, left/right hashes:

The adapter verifies membership in the client-committed rows, but it does not enforce uniqueness between different indexes. The manifest builder rejects duplicates because one physical sector piece may carry multiple notifications. If the commitment contained the same (PieceCID, paddedSize) row more than once, those notifications could satisfy several bitmap indexes without several stored copies. A provider cannot add a row that the client did not commit, but the contract does not protect a client that deliberately commits duplicate rows.

leaf = keccak256(abi.encode(keccak256("PoRepMarket.PieceSet.Leaf"), uint8(1), index, digest, size))
empty = keccak256(abi.encode(keccak256("PoRepMarket.PieceSet.Empty"), uint8(1)))
node = keccak256(abi.encode(keccak256("PoRepMarket.PieceSet.Node"), left, right))
manifestHash = keccak256(abi.encode(
  keccak256("PoRepMarket.PieceSet.Commitment"), uint8(1), uint32(pieceCount), requestedSizeBytes, root
))

Each FIP-0109 notification payload is abi.encode(uint256 dealId, uint32 pieceIndex, uint32 pieceCount, bytes32[] proof). Activation requires every unique index and the exact requested byte total. This adapter stores the authenticated PieceCID, sector number, padded size, and minimum commitment epoch for every accepted index. Exact callback replay is a no-op. A replay that changes any stored placement field is rejected.

Before sealing, SP tooling must fetch the complete manifest, recompute manifestHash, verify that the padded sizes sum to requestedSizeBytes, and prepare exactly one notification for each row. The ProveCommit or replica-update call must set require_notification_success to true; otherwise a rejected notification does not abort sector activation and there is no callback retry path. The commitment proves membership in the client-selected piece set, not retrievability, continued storage after initial placement, or physical placement exclusivity between different deals.

Sector evidence refresh

PoRepMarket.refreshEvidenceStatus is permissionless. The caller supplies the current deadline and partition for each consecutive sector selected by the adapter's stored cursor:

struct SectorLocation {
    int64 deadline;
    int64 partition;
}

abi.encode(SectorLocation[] locations)

The adapter verifies each stored sector through FIP-0112 and reads its nominal expiration. It returns PARTIAL when the batch stops before the end of the sector list. Partial progress does not replace the last completed status used by settlement. A complete sweep publishes ACTIVE only when the accumulated sector bytes equal the authenticated manifest receipt. One verified Faulty or Dead sector publishes COVERED_BYTES_MISMATCH with zero covered bytes. A bad location or actor-call failure reverts and preserves the previous completed status.

Operators read getRefreshState, getSectorCount, and getSectorNumber, resolve the current sector locations off chain, estimate the exact refresh transaction, and submit as many consecutive locations as fit. There is no contract-level piece, sector, or batch cap. Repeat until EvidenceRefreshCompleted reports ACTIVE or COVERED_BYTES_MISMATCH. getPiecePlacement and getSectorCoveredBytes let a new operator reconstruct the placement inventory from chain state without the original manifest host or a private database.

Stale or never-refreshed (INACTIVE) evidence makes settlement revert with EvidenceTooStale() without advancing its stored settlement cursor. A later completed ACTIVE sweep lets the same interval be retried. A coverage mismatch from a Faulty or Dead sector settles the interval with zero payment and advances the cursor, as the DataCap adapter does for terminated claims. A recovered Faulty sector is paid again from the next ACTIVE sweep. This adapter relies on the current pre-Re-Snap rule that sector content cannot change while the sector number stays Active. It must be replaced or upgraded with authenticated current-content membership before Re-Snap is enabled for these deals.

Deal lifecycle

stateDiagram-v2
    [*] --> Accepted: proposeDeal / reserve provider capacity

    Accepted --> Accepted: create Validator and prepare FilecoinPay rail
    Accepted --> Active: activateEvidence / commit covered capacity and start payment
    Accepted --> Rejected: reject before rail creation / release reserved capacity
    Accepted --> Terminated: terminate early / release reserved capacity

    Active --> Active: refresh evidence and settle FilecoinPay rail
    Active --> Finalized: finalize after service end / release committed capacity
    Active --> Terminated: terminate early / release committed capacity

    Rejected --> [*]
    Finalized --> [*]
    Terminated --> [*]
Loading

proposeDeal selects and reserves a provider offer. proposeDealWithSpecificOffer lets an administrator reserve a specific offer. Both create an ACCEPTED deal with a snapshot of the selected provider, payment terms, promised SLIs, and reserved bytes.

While a deal is ACCEPTED, its client creates a Validator through ValidatorFactory, approves it as a FilecoinPay operator, and calls Validator.createRail. The client posts DataCap through the deal's evidence adapter. The PoRep service then submits claim evidence and calls activateEvidence. Successful activation commits the covered capacity, moves the deal to ACTIVE, sets its service window, and starts payment.

While the deal is ACTIVE, any caller can call refreshEvidenceStatus to keep evidence current. FilecoinPay calls Validator.validatePayment, which asks PoRepMarket.validateDealSettlement for the accepted amount and settlement epoch.

After the service end epoch, finalizeDeal terminates the rail and releases committed capacity. terminateDeal handles early termination from a prepared or active rail. rejectAcceptedDeal releases reserved capacity for an accepted deal that does not yet have a rail.

Important invariants

  • Provider offers may change, but the provider, payment terms, promised SLIs, and reserved bytes copied into a deal do not follow later offer changes.
  • Deal state, evidence status, and FilecoinPay rail status advance through separate contract calls.
  • The evidence adapter owns evidence-specific facts. PoRepMarket owns deal state, settlement decisions, and settlement accounting.
  • Provider capacity is reserved when the deal is created, committed using covered bytes on activation, and released on rejection, termination, or finalization.
  • Validator implements FilecoinPay's validation interface, while PoRepMarket.validateDealSettlement computes the accepted amount and updates settlement progress.

Development and deployment commands

The justfile is the command reference. Run just --list to see the commands supported by the current checkout. Deployment behavior is implemented in script/deployment.ts.

Run just pre-push before pushing changes.

About

No description, website, or topics provided.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages