Skip to content
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

Merged
merged 5 commits into from
Jan 8, 2020
Merged

add slashing reward mechanism #789

merged 5 commits into from
Jan 8, 2020

Conversation

zixuanzh
Copy link
Contributor

@zixuanzh zixuanzh commented Jan 8, 2020

TODO:

  • define arithmetic precision and int types
  • pledge collateral slashing from detected fault (equivalent of UncommittedPowerFault)

@zixuanzh zixuanzh requested a review from anorth January 8, 2020 05:35
@@ -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) {
Copy link
Member

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.

Copy link
Contributor Author

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

Copy link
Contributor

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
Copy link
Member

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.

Copy link
Contributor Author

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)
Copy link
Member

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.

Copy link
Contributor Author

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.

Copy link
Contributor

@sternhenri sternhenri left a 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.
Copy link
Contributor

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...

Copy link
Contributor Author

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

Copy link
Contributor

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?

Copy link
Contributor Author

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) {
Copy link
Contributor

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
Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor

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 {
Copy link
Contributor

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
Copy link
Contributor

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...

@zixuanzh zixuanzh merged commit 165c80c into master Jan 8, 2020
@zixuanzh zixuanzh deleted the slashing-reward branch January 8, 2020 20:03
@zixuanzh zixuanzh mentioned this pull request Jan 9, 2020
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants