Skip to content

feat: operational pricing#488

Open
wjmelements wants to merge 14 commits into
mainfrom
operation-pricing
Open

feat: operational pricing#488
wjmelements wants to merge 14 commits into
mainfrom
operation-pricing

Conversation

@wjmelements
Copy link
Copy Markdown
Contributor

Reviewer @rvagg
Implements #469
One difficulty of this task is that one-time payments have to come from the fixed lockup, and, after termination, the lockup cannot be increased further.
One design goal is to avoid reading getRail, which reads many fields.
Another goal is to limit the additional calls to modifyRailPayment and modifyRailLockup.
The main approach is to aggregate the fees into batches.
Fixed lockup is replenished when the lockup (lifecycle reserve) gets low.
It can also be prefunded by topUpLifecycleReserve
I also remove the sybil fee (briefly discussed here) in a separate commit (allowing an easy revert if necessary).

Changes

  • remove sybil fee
  • add create dataset fee, add pieces fee, remove pieces fee, and terminate dataset fee
  • add topUpLifecycleReserve

Assisted-by: Claude:claude-sonnet-4-6
Assisted-by: Claude:claude-sonnet-4-6
Assisted-by: Claude:claude-sonnet-4-6
Assisted-by: Claude:claude-sonnet-4-6
Assisted-by: Claude:claude-sonnet-4-6
@wjmelements wjmelements requested a review from rvagg May 28, 2026 00:52
@FilOzzy FilOzzy added this to FOC May 28, 2026
@github-project-automation github-project-automation Bot moved this to 📌 Triage in FOC May 28, 2026
@wjmelements wjmelements added the enhancement New feature or request label May 28, 2026
@wjmelements wjmelements moved this from 📌 Triage to 🔎 Awaiting review in FOC May 28, 2026
@BigLep BigLep requested a review from Copilot May 28, 2026 06:23
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements per-operation pricing for FilecoinWarmStorageService by introducing one-time operation fees paid from a fixed “lifecycle reserve” on the PDP rail, while also removing the previously-charged sybil fee burn-rail flow.

Changes:

  • Add USDFC operation-fee constants (create dataset / add pieces / schedule removals / consent termination) plus lifecycle reserve target + replenishment threshold.
  • Track and batch-flush pending one-time fees via updateStorageRates, including reserve replenishment via modifyRailLockup.
  • Add Foundry tests covering fee accumulation/flush and replenishment behavior; remove sybil-fee tests and docs.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
SPEC.md Documents operation fees and lifecycle reserve batching/replenishment behavior.
README.md Updates pricing overview to include one-time operation fees and lifecycle reserve concept.
CHANGELOG.md Removes sybil-fee related release notes.
service_contracts/src/lib/PriceListUSDFC.sol Adds operation-fee constants and lifecycle reserve parameters; removes SYBIL_FEE.
service_contracts/src/lib/Rails.sol Seeds lifecycle reserve on rail creation; updates updateStorageRates to flush one-time fees and replenish lockup.
service_contracts/src/FilecoinWarmStorageService.sol Adds pending-fee + reserve accounting, operation-fee accrual points, topUpLifecycleReserve, and updated rate/fee flush flow.
service_contracts/src/lib/FilecoinWarmStorageServiceStateLibrary.sol Extends dataset view struct decoding to include pending fees + reserve balance.
service_contracts/src/lib/FilecoinWarmStorageServiceStateInternalLibrary.sol Same as above for the internal (generated) view library.
service_contracts/src/lib/FilecoinWarmStorageServiceLayout.json Updates storage layout to include the new packed slot for fee/reserve fields.
service_contracts/abi/FilecoinWarmStorageService.abi.json Adds ABI entry for topUpLifecycleReserve.
service_contracts/abi/FilecoinWarmStorageServiceStateView.abi.json Extends DataSetInfo view structs with pending fees + reserve balance fields.
service_contracts/abi/FilecoinWarmStorageServiceStateLibrary.abi.json Same ABI struct extension for the library ABI.
service_contracts/test/FilecoinWarmStorageService.t.sol Updates expectations for removed burn rail and new lifecycle reserve lockup assumptions; removes sybil-fee tests.
service_contracts/test/OpFees.t.sol Adds new tests validating fee accrual/flush and lifecycle reserve replenishment behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread SPEC.md
Comment thread service_contracts/src/FilecoinWarmStorageService.sol
Comment thread service_contracts/src/FilecoinWarmStorageService.sol Outdated
Comment thread service_contracts/src/FilecoinWarmStorageService.sol
Comment thread service_contracts/src/FilecoinWarmStorageService.sol
Comment thread service_contracts/src/FilecoinWarmStorageService.sol
Comment thread SPEC.md
@rvagg
Copy link
Copy Markdown
Collaborator

rvagg commented May 28, 2026

Some good comments from copilot in there.

I have two main thoughts:

First: The 0.1 sybil was still serving a purpose, as per the comment in there and as described in https://www.notion.so/filecoindev/Minimising-Client-Griefing-Potential-via-SP-Deposit-36bdc41950c180a283b3c4caf39c0895 - with only a data set creation fee it's cheap to cause an SP to lock up considerable FIL for 30+ days. Roughly 1:40: ~$100 of create fees locks up ~4000 FIL of SP capital.

Luca suggested a matched client-side lockup (0.1 USDFC, returned 30 days after termination) and the lifecycle reserve is essentially that, locked at creation. My hesitation as mentioned in response to Luca in the doc is that the costs are still different on both sides: lockup costs the attacker basically nothing (they just wait and get it back), while the attacked SP has a drained wallet and is denying service to all current and future clients (including proving) until they top-up or wait 30 days. So the question for you and Luca: is a matched lockup a clear enough disincentive on its own, or do we want a burned component on top, also what do we suggest to SPs on how much FIL to hold now? No strong opinion on mechanism as long as we don't have such an obvious DoS vector.

Second: Batching the rail changes is clever, but the deferral brings us back to the nextProvingPeriod bricking problem, which we can't have. When the client isn't properly funded by the time of that call, or we are in lockup, the SP can't prove and is out of pocket on the fees due to them. We could best-effort with try/catch and cap-and-carry for the pending, but best-effort to give the SP the fees due to them isn't a great protocol.

Ideally every client-incurred fee is secured from the client at the moment they incur it. The revert belongs on the client's transaction, not the SP's, and the SP is never out of pocket because a client found a loophole.

  • addPieces already does a rate-change modifyRailPayment, all good.
  • schedulePieceRemovals could call modifyRailLockup to cover but not modifyRailPayment, I believe it's the cheaper of the two and there's already an incentive for batched calls because the client fee is flat. If the client can't fund it their tx reverts. Then nextProvingPeriod only ever draws the one-time down out of lockup that's already there, covered by construction, so it can't revert on funds.
  • create and terminate fees are not as problematic so we can avoid securing them up front, but we'd need to flush before termination just to clear them out (as per comments inline).

@lucaniz
Copy link
Copy Markdown

lucaniz commented May 28, 2026

Luca suggested a matched client-side lockup (0.1 USDFC, returned 30 days after termination) and the lifecycle reserve is essentially that, locked at creation. My hesitation as mentioned in response to Luca in the doc is that the costs are still different on both sides: lockup costs the attacker basically nothing (they just wait and get it back), while the attacked SP has a drained wallet and is denying service to all current and future clients (including proving) until they top-up or wait 30 days. So the question for you and Luca: is a matched lockup a clear enough disincentive on its own, or do we want a burned component on top,

I see your point, which basically says: "even if client locks, then client does not have to operate, while SP does", which is a fair point.

I think that adding a burnt part on the client side could make sense in the end. Curious to know what others think specifically on this. The high level reasoning on my side is that having some burn cost other that capital cost on the client side could be a disincentive in DoSsing the SP.
That would mean getting back to a variation of the "old" formulation of sybil fee to some extent.

See this as a reference of old formulation

@lucaniz
Copy link
Copy Markdown

lucaniz commented May 29, 2026

After further conversation with @rvagg, I have a more complete picture.
Basically, my understanding is that it's gas expensive to touch Filecoin pay and so burning of sybil fee wouldbring over technical complexity (@wjmelements please jump in if needed, you are much more expert than me here).

My opinion is the following:

  • From the protocol perspective the burn is the "best" approach in order to prevent misbehavior as there is an actual cost which goes beyond capital.

  • If we can not/dont want to do it b/c of technical reasons, this is fair, but we need to pick one of the two: either giving up on stronger protection against malicious clients or accepting complexity.

My personal intuition is that, at least at the beginning, it is not out of mind to assume that clients wont be that malicious/SP and client somehow trust each other. But it can be i'm not viewing the full picture.

If we agree on the above, we can stay as we are, avoiding complexity, flag the potential abuse and keep the sybil burn as a potential patch in the future (if needed).

…istent and terminated datasets

Assisted-by: Claude:claude-sonnet-4-6
@wjmelements
Copy link
Copy Markdown
Contributor Author

wjmelements commented May 29, 2026

Second: Batching the rail changes is clever, but the deferral brings us back to the nextProvingPeriod bricking problem, which we can't have. When the client isn't properly funded by the time of that call, or we are in lockup, the SP can't prove and is out of pocket on the fees due to them. We could best-effort with try/catch and cap-and-carry for the pending, but best-effort to give the SP the fees due to them isn't a great protocol.

I agree that we should ensure nextProvingPeriod cannot fail. Because the payments are secured by the fixed lockup, I believe the only step that can fail is the lifecycle replenishment, which is unrelated to the batch payment design. I'm considering two workarounds:

  • replenish during the operation instead of nextProvingPeriod
  • try-catch the replenishment in nextProvingPeriod

…s and add depletion test

Assisted-by: Claude:claude-sonnet-4-6
…ay settle period

Assisted-by: Claude:claude-sonnet-4-6
@wjmelements
Copy link
Copy Markdown
Contributor Author

I am quite interested in removing the burn rail.

Roughly 1:40: ~$100 of create fees locks up ~4000 FIL of SP capital.

2.5% return is pretty good for one month.

@lucaniz @rvagg Can we improve the cleanup DOS vector by increasing the create data set fee?

@rvagg
Copy link
Copy Markdown
Collaborator

rvagg commented May 30, 2026

I did some measurement in devnet of the cost of the burn rail last night. At fresh devnet gas using the stand-alone createDataSet we go from 228M with the burn rail to 130M without it, so it's consuming ~42% of the gas, at least in devnet (I'm unsure how the storage costs would scale that on mainnet, would FP be more expensive to operate or less than PDP+FWSS?). So yeah, that biases me toward removing it too.

Let's proceed without it for now and talk about possibly bumping the create fee to the SP; not so high that it's a client-farming incentive and we can frame it as both an incentive, reimbursement for the createDataSet gas and maybe the administrative hassle of setting up a new data set to manage.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

Status: 🔎 Awaiting review

Development

Successfully merging this pull request may close these issues.

5 participants