-
Notifications
You must be signed in to change notification settings - Fork 175
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Address changes to FIP 98 while in last call #1128
base: master
Are you sure you want to change the base?
Address changes to FIP 98 while in last call #1128
Conversation
Co-authored-by: Rod Vagg <rod@vagg.org>
Co-authored-by: Rod Vagg <rod@vagg.org>
I believe the test cases section should be expanded as well, especially given 2 new special cases. |
Current changes (without exports and more detailed test cases) based on this proposal: https://github.com/filecoin-project/builtin-actors/compare/lesnyrumcajs/fip-0098v2?expand=1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably best for Rod's comments to be addressed before further reviews, but a more general ask: when making changes during last call, it would be advisable to use the PR description to explain what changes are proposed and why.
FIPS/fip-0098.md
Outdated
In addition to the revised termination fee formulas, a few built-in actor methods need to be created and exported so they are accessible from FEVM: | ||
| Function Name | Function Signature | Description | | ||
|--------------|-------------------|-------------| | ||
| `get_miner_power` | `get_miner_power(miner_id: ActorID) -> (QAPower, RawPower)` | Returns the miner's quality-adjusted and raw power | | ||
| `get_miner_initial_pledge` | `get_miner_initial_pledge(miner_id: ActorID) -> TokenAmount` | Returns the miner's total initial pledge amount | | ||
| `get_network_projection` | `get_network_projection(projection_period_start: ChainEpoch, projection_period_end: ChainEpoch) -> (Power, TokenAmount)` | Returns the network's projected power and reward for the given projection period | | ||
| `get_termination_fee_percentage` | `get_termination_fee_percentage() -> f64` | Returns the network's termination fee percentage (8.5%) | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@LesnyRumcajs this probably means this value should be in the actors policy framework rather than just a plain constant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this going to be variable across different networks? I'm not opposed to this, but I'd like to understand what makes some constants get promoted to the actors policy from plain constants.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not the best person to answer where these variables should live, but I don't think it's necessary to have per network values for these variables. Actually seems more useful to always keep them in sync.
Co-authored-by: Rod Vagg <rod@vagg.org>
Co-authored-by: Rod Vagg <rod@vagg.org>
FIPS/fip-0098.md
Outdated
- When terminating a single sector: | ||
- Not considering fault fees, for a sector where its age >= `TERMINATION_LIFETIME_CAP`, termination fee should equal `TERM_PENALTY_PLEDGE_PERCENTAGE * initial pledge` | ||
- Not considering fault fees, for a sector where its age < `TERMINATION_LIFETIME_CAP`, termination fee should equal `TERM_PENALTY_PLEDGE_PERCENTAGE * of initial pledge * sector age in days / TERMINATION_LIFETIME_CAP` | ||
- Considering fault fees, for a sector with a termination fee that is less than the associated sector's fault fee, termination fee should equal `FAULT_FEE_MULTIPLE * fault fee` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this correct? The currently proposed bounds in this PR suggest it's the other way around; if the termination fee is larger than the fault_fee * TERM_FEE_MAX_FAULT_FEE_MULTIPLE
, it's bound to to that max. If the termination fee is smaller, it gets limited only by the TERM_FEE_MIN_PLEDGE_MULTIPLE
fault_fee = pledge_penalty_for_continued_fault(sector_power)
maximum_fee = fault_fee * TERM_FEE_MAX_FAULT_FEE_MULTIPLE // (1.05 * fault_fee)
termination_fee = max(minimum_fee, min(age_adjusted_fee, maximum_fee))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@LesnyRumcajs really good catch here - my tired eyes failed me last night. I fixed in a31032e
Noting that the fault_fee and min_termination_fee are not actually discounted by sector day age. Should we be applying the young-sector-age discount to fault fees? I don't think so, but @irenegia might be a good person to weigh in on this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's wait for @irenegia, then.
FIPS/fip-0098.md
Outdated
(the below 2 methods could also be built as a FEVM native oracle contract instead of as a built-in actor method with exports to access from FEVM): | ||
| `single_fault_fee` | `single_fault_fee(qa_power: QAPower) -> TokenAmount` | Returns the fault fee calculation for a given amount of QA power | | ||
| `max_termination_fee` | `max_termination_fee(initial_pledge: TokenAmount, power: QAPower) -> TokenAmount` | Returns the maximum termination fee calculation for a given initial pledge and power amount | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any existing example of such an approach? Is this required?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you asking about the max_termination_fee method? I'm not sure I understand what you're asking, but there wouldn't be an example as this FIP is now creating a new way to compute that
From my perspective, this is the single most important part of the fip - it allows FEVM actors to get a collateral value for a miner cc @anorth for visibility here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean an example of FEVM native oracle contract
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oooh, not off the top of my head, but i think the name is fancier than the concept - just a singleton FEVM actor that:
- Exposes methods like
max_termination_fee
- When called, makes the associated calls to exported built-in actor methods to gather the building blocks to compute the answer
- Computes the answer and returns it to the caller
In my book, this is "less good" than just including these methods in built-in actors because it will be harder / messier to maintain. Also potentially harder to upgrade in the future, but it is possible
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thoughts below but my main question is: how are you planning on using these methods? On-chain? Off-chain? Per-sector? For batches of sectors? That'll impact exactly what the final design should look like.
I'd much prefer to just have a built-in method so we can change it on network upgrades. I'd prefer not to expose any network_projection
or termination_fee_percentage
methods, instead exposing just a max_termination_fee
method (and maybe a single_fault_fee
method). I don't want to expose too many of the internals of how these fees are calculated in case they change in the future.
I'd prefer to expose these methods in terms of "given a set of sectors X/Y/Z, what is the max termination fee?", but I think it's probably also safe to do this in terms of power/pledge (we just have to guarantee that we'll never take any other per-sector parameters into account, but I think that's safe).
In terms of accuracy, the max_termination_fee
isn't going to be 100% accurate if called for a batch of sectors (sum of power, sum of pledge). It'll still be correct when called sector-by-sector.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Stebalien thanks for chiming in here - this will be used on-chain to get the collateral value of a miner. It would look something like this in pseudo code:
var minerAddress = 0x0000000000....xxx
var minerID = xxx
var minerBalance = address(minerAddress).balance
var minerIP = precompiles.miner_initial_pledge(minerID)
var minerPower = precompiles.miner_power(minerID)
var minerMaxTermFee = precompiles.max_termination_fee(minerIP, minerPower)
var minerCollateralValue = minerBalance - minerMaxTermFee
var minerDebt = fetchFromContractToGetDebt(minerID)
var isUnderCollateralized = minerDebt > minerCollateralValue
if isUnderCollateralized { ... do something ... }
else { ... do something ...}
Something like that.
It is quite useful to be able to get power and initial pledge for other reasons, but ultimately the most important thing to come from this fip is a supported method to get the max termination fee that is supported by the core devs, upgraded as it needs to, and reports reliable enough info to rely on. It would be easier UX if the method took the minerID instead of IP and power, but IP and power are more composable primitives, per @anorth point in a previous message
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I concur with @Stebalien I'd rather not expose the primitive methods, just the useful higher level ones (apologies if I suggested otherwise elsewhere, @Schwartz10). I do recall that I suggested the max_termination_fee method take pledge and power as parameters though; I think that makes it more useful since it can be used for one or more sectors, as well an entire miner, so long as the caller knows or can get the associated power and pledge.
But i'm not wedded to that – happy to let @Stebalien make a final decision.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unasked question about what actor to put these two final methods on. It'll need to call to both power and reward actor. The power actor would be a reasonable singleton place IMO, but could be convinced otherwise.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree. So:
- Add a method to the power actor to get a miner's QA power. We currently only export a method to get the miner's raw byte power (i.e., the amount of space they're proving).
- Add a method to the miner actor to get the initial pledge.
- Add a
MaxTerminationFee
method to the power actor.
@@ -71,6 +125,18 @@ For all new sectors or snapped sectors, these three values are set to zero. | |||
|
|||
These redundant fields can be removed from state in a future migration. | |||
|
|||
In addition to the revised termination fee formulas, a few built-in actor methods need to be created and exported so they are accessible from FEVM: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might make sense to add another column - which actor this method belongs to. This would also affect signatures. As I see it:
power
:miner_power
,miner
:miner_initial_pledge
,- ?:
termination_fee_percentage
, it's just a constant defined in theminer
, so I guess there? - ?:
network_projection
, could you please point me to a more specific implementation of this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cc @anorth for help / assistance on this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The network projection couldn't easily be a single call, as different actors have the state for each part:
- Reward actor has the reward estimator
- Power actor has the power estimator
AFAIK the only possible projection_period_start is the current epoch, so that cannot be a parameter to either.
But I think these are probably too low level. The discussion below suggests a maxTerminationPenalty method which sounds better. Whatever actor it's placed on, it will have to call to the reward and power actors to get the estimators.
We don't have prior art for where to put a constant, since actors all just have them compiled in.
Perhaps put the termination fee % on the power actor, as a well-defined singleton. But if @Stebalien or @ZenGround0 suggest something else, go with that. Or just don't expose this constant, abstract over it with a method for computing termination fees.
Co-authored-by: Hubert <lesny.rumcajs@gmail.com>
@@ -71,6 +125,18 @@ For all new sectors or snapped sectors, these three values are set to zero. | |||
|
|||
These redundant fields can be removed from state in a future migration. | |||
|
|||
In addition to the revised termination fee formulas, a few built-in actor methods need to be created and exported so they are accessible from FEVM: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The network projection couldn't easily be a single call, as different actors have the state for each part:
- Reward actor has the reward estimator
- Power actor has the power estimator
AFAIK the only possible projection_period_start is the current epoch, so that cannot be a parameter to either.
But I think these are probably too low level. The discussion below suggests a maxTerminationPenalty method which sounds better. Whatever actor it's placed on, it will have to call to the reward and power actors to get the estimators.
We don't have prior art for where to put a constant, since actors all just have them compiled in.
Perhaps put the termination fee % on the power actor, as a well-defined singleton. But if @Stebalien or @ZenGround0 suggest something else, go with that. Or just don't expose this constant, abstract over it with a method for computing termination fees.
FIPS/fip-0098.md
Outdated
(the below 2 methods could also be built as a FEVM native oracle contract instead of as a built-in actor method with exports to access from FEVM): | ||
| `single_fault_fee` | `single_fault_fee(qa_power: QAPower) -> TokenAmount` | Returns the fault fee calculation for a given amount of QA power | | ||
| `max_termination_fee` | `max_termination_fee(initial_pledge: TokenAmount, power: QAPower) -> TokenAmount` | Returns the maximum termination fee calculation for a given initial pledge and power amount | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I concur with @Stebalien I'd rather not expose the primitive methods, just the useful higher level ones (apologies if I suggested otherwise elsewhere, @Schwartz10). I do recall that I suggested the max_termination_fee method take pledge and power as parameters though; I think that makes it more useful since it can be used for one or more sectors, as well an entire miner, so long as the caller knows or can get the associated power and pledge.
But i'm not wedded to that – happy to let @Stebalien make a final decision.
Fix formatting Fix 2 edge cases typo
@anorth @Stebalien @LesnyRumcajs I removed some methods that were "low level" in favor of the necessary ones from an FEVM perspective. You could also get rid of |
FIPS/fip-0098.md
Outdated
|--------------|-------------------|-------------| | ||
| `miner_power` | `miner_power(miner_id: ActorID) -> (QAPower, RawPower)` | Returns the miner's quality-adjusted and raw power | | ||
| `miner_initial_pledge` | `miner_initial_pledge(miner_id: ActorID) -> TokenAmount` | Returns the miner's total initial pledge amount | | ||
| `termination_fee_percentage` | `termination_fee_percentage() -> f64` | Returns the network's termination fee percentage (8.5%) | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we include this method, should the return type be f64
? I'm not sure how big of a deal this is, but it's a floating point type, so it might not return exactly 0.085. I'm not sure we have a better way of handling those anywhere. Perhaps it should return the numerator and denominator (how it is handled in the code).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Schwartz10 I went ahead with my suggestion in filecoin-project/builtin-actors#1639. Does this work for you? Is everyone in agreement that this method should be there at all?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point - i dont mind returning two values for num and denom, but whatever those values return will likely end up getting converted into uint256
types in solidity, so ideally each returned number is convertible into uint256
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They are u32
for now, so it should be fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated 074cc7d
FIPS/fip-0098.md
Outdated
| Function Name | Function Signature | Description | | ||
|--------------|-------------------|-------------| | ||
| `miner_power` | `miner_power(miner_id: ActorID) -> (QAPower, RawPower)` | Returns the miner's quality-adjusted and raw power | | ||
| `miner_initial_pledge` | `miner_initial_pledge(miner_id: ActorID) -> TokenAmount` | Returns the miner's total initial pledge amount | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given miner_initial_pledge
lives under miner
, it probably makes sense to rename it to initial_pledge
and omit the parameters (the parameter is implicitly this
/self
). But maybe it's an implementation detail and doesn't matter in the context of this FIP.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
from the context of the fevm smart contracts we definitely want to be able to pass a parameter
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please elaborate? Why should it have a miner_id
parameter if it's a method of a particular miner? To my understanding, it's the same as, e.g., get_peer_id
, which doesn't take any parameters. Or should it live as a method of another actor?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updates to FIP 98 while in last call: