-
Notifications
You must be signed in to change notification settings - Fork 170
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
add slashing reward mechanism #789
Conversation
@@ -180,7 +180,7 @@ func (a *StoragePowerActorCode_I) OnMinerSurprisePoStFailure(rt Runtime, numCons | |||
} | |||
} | |||
|
|||
func (a *StoragePowerActorCode_I) ReportConsensusFault(rt Runtime, slasherAddr addr.Address, faultType ConsensusFaultType, proof []block.Block) { | |||
func (a *StoragePowerActorCode_I) ReportConsensusFault(rt Runtime, faultType ConsensusFaultType, proof []block.BlockHeader) { |
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 hope that we can remove visibility of the BlockHeader type here and instead delegate the fault checking to the runtime. I'll file an issue for it.
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 check here was supposed to be done in ExpectedConsensus. Does this mean that that logic will now also live in the runtime? I am inclined to leave this as a follow up, there might be other places in actor that require similar validation cc @sternhenri
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.
Mmmh I now understand your question from yesterday (out of band) @zixuanzh. Yeah, we can make it work that way, we'll just need to special case the message validation in the runtime in order to run this check...
So maybe change this to ReportVerifiedConsensusFault
passing in the slasheeAddr as an arg (instead of proof) @zixuanzh and we'll write the verif code in the runtime with the proof which then calls this method.
collateralToSlash := st._getPledgeSlashForConsensusFault(currPledge, faultType) | ||
slasherShare := _getConsensusFaultSlasherShare(elapsedEpoch) | ||
Assert(slasherShare <= 1 && slasherShare > 0) | ||
amountToSlasher := actor.TokenAmount(slasherShare) * collateralToSlash |
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.
Blocking: TokenAmount
is an integer so the cast will clamp to either 0 or 1, which isn't what we want.
I have a strong preference for reworking this arithmetic in terms of bigints. Rather than _getConsensusFaultSlasherShare returning a float, have it accept collateralToSlash
as a parameter and compute the share using integer multiplication and division. Perhaps we can represent the constants as numerator/denominator ratios.
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 sure on how to do BigInt arithmetic correctly atm, so I have updated the logic and left them in comment form.
UpdateRelease(rt, h, st) | ||
|
||
// reward slasher | ||
rt.SendFunds(slasherAddr, amountToSlasher) |
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.
Blocking: without subtracting amountToSlasher
from the balance table this will burn too much funds when the actor is deleted.
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.
sorry about this, this has been fixed.
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.
a proposed fix, but otherwise LGTM
@@ -201,8 +201,7 @@ This is detectable when a given miner submits two blocks that satisfy any of the | |||
- (3) `parent-grinding fault`: one block's parent is a Tipset that provably should have included a given block but does not. While it cannot be proven that a missing block was willfully omitted in general (i.e. network latency could simply mean the miner did not receive a particular block), it can when a miner has successfully mined a block two epochs in a row and omitted one. That is, this condition should be evoked when a miner omits their own prior block. When a miner's block at epoch e + 1 references a Tipset that does not include the block they mined at e both blocks can be submitted to prove this fault. | |||
{{< diagram src="diagrams/parent_grinding.dot.svg" title="Parent-Grinding fault" >}} | |||
|
|||
Any node that detects any of the above events should submit both block headers to the `StoragePowerActor`'s `ReportConsensusFault` method. The "slasher" will receive a portion (TODO: define how much) of the offending miner's {{<sref pledge_collateral>}} as a reward for notifying the network of the fault. | |||
(TODO: FIP of submitting commitments to block headers to prevent miners censoring slashers in order to gain rewards). | |||
Any node that detects any of the above events should submit both block headers to the `StoragePowerActor`'s `ReportConsensusFault` method. The "slasher" will receive a portion of the offending miner's {{<sref pledge_collateral>}} as a reward for notifying the network of the fault. Consensus faults (except for `uncommitted power fault` below which falls under storage faults with impact on consensus) will tentatively result in all pledge collateral being slashed and the miner removed from the power table. Some portion of the pledge collateral is given to the slasher as a function of some initial share (`SLASHER_INITIAL_SHARE`) and growth rate (`SLASHER_SHARE_GROWTH_RATE`). Slasher's share of the slashed collateral increases as block elapses since the block when the fault is committed. Default growth rate results in slasher's share reaches 1 after 30 blocks. However, only the first slasher gets its share of the pledge collateral and the remaining pledge collateral will be burned. The longer a slasher waits, the higher the likelihood that the slashed collateral will be claimed by another slasher. |
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 like this incentive structure. Note that 30 blocks will be 22.5 minutes which seems a bit short to me. I could see slashes coming on the order of finality/2, so closer to something like 250 blocks or something...
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.
will update this to 250 blocks for now but lets keep in mind that they are placeholders and we should examine them more carefully during testnet
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.
reaplace number with the variable name and add variable to network_params.go
?
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.
250 is derived from variables (growth rate and initial share) in network params
@@ -180,7 +180,7 @@ func (a *StoragePowerActorCode_I) OnMinerSurprisePoStFailure(rt Runtime, numCons | |||
} | |||
} | |||
|
|||
func (a *StoragePowerActorCode_I) ReportConsensusFault(rt Runtime, slasherAddr addr.Address, faultType ConsensusFaultType, proof []block.Block) { | |||
func (a *StoragePowerActorCode_I) ReportConsensusFault(rt Runtime, faultType ConsensusFaultType, proof []block.BlockHeader) { |
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.
Mmmh I now understand your question from yesterday (out of band) @zixuanzh. Yeah, we can make it work that way, we'll just need to special case the message validation in the runtime in order to run this check...
So maybe change this to ReportVerifiedConsensusFault
passing in the slasheeAddr as an arg (instead of proof) @zixuanzh and we'll write the verif code in the runtime with the proof which then calls this method.
TODO() | ||
// BigInt Operation | ||
// var growthRate = node_base.SLASHER_SHARE_GROWTH_RATE_NUM / node_base.SLASHER_SHARE_GROWTH_RATE_DENOM | ||
// var multiplier = growthRate^elapsedEpoch |
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.
you probably want (1+growthRate)^elapsedEpoch - 1
otherwise your number will get smaller with elapsed epochs.
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.
ah growth rate is greater than 1 here so we good.
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.
nit: feels to me a growth rate should be between 0 and 1
} | ||
} | ||
|
||
func _getConsensusFaultSlasherReward(elapsedEpoch block.ChainEpoch, totalPledge actor.TokenAmount) actor.TokenAmount { |
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.
totalPledge
should maybe be slashedCollat
since it may not be the total pledge.
func _getConsensusFaultSlasherReward(elapsedEpoch block.ChainEpoch, totalPledge actor.TokenAmount) actor.TokenAmount { | ||
TODO() | ||
// BigInt Operation | ||
// var growthRate = node_base.SLASHER_SHARE_GROWTH_RATE_NUM / node_base.SLASHER_SHARE_GROWTH_RATE_DENOM |
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.
float issue here but no clean way around that I can tell...
271cc3e
to
218d29a
Compare
TODO: