unit-e
unit-e copied to clipboard
Improved proposal randomness
From the original consensus design decisions meeting notes:
Kernel hash depends on last finalized block or last block
In the current implementation derived form PoS v3 the proposing algorithm is as follows:
foreach (utxo in wallet) {
kernelhash = hash(prevBlock.stakeModifier ++ utxo.time ++ utxo.hash ++ utxo.n ++ blockTime)
if (kernelhash < difficulty * utxo.value) {
block.stakeModifier = hash(kernelhash ++ prevBlock.stakeModifier)
propose(block);
return;
}
}
That is, the kernel hash depends on the stake modifier of the coin/utxo used as stake and the previous block's stake modifier (i.e. the previous/last block).
This issue is about whether finalized checkpoints should be taken into account
aka "Improved proposal randomness" - we definitely wanna handle this issue. @amiller wrote about it and should assist in design/implementation
As David Tse pointed out, when we do not take into account block transactions to compute the stake modifier, then there's the risk of bribing k
proposers to rewrite the blocks they proposed, so it would be possible to change the (recent) history without having to hold the 50% of staking power.
As we know, there are some reasons to make the stake modifier not dependant on the block transactions: decreasing the amount of randomness sources in a way that PoS can't degenerate into PoW, and to keep the property were the ability to propose is tied to the proportion of stake is being used.
Having said that, we could try to pick the best of both worlds. The stake modifier could take into account the snapshot hash at the height of the last justified ~(or finalized)~ block, so then it won't be possible to add or remove outputs before that point. So it wouldn't matter if someone rewrites history before that point because even that rewrite must match a well-known & well-defined state at certain point.
Note that I'm referring to the snapshot hash and not to the Merkle root because the Merkle root for a specific block does not tell us enough about the global state of the chain.
EDIT:
Although I was too optimistic when I wrote this response, and this proposal still leaves some space to the attackers to modify blocks after the justified heights (a "small" time window to the attacker to identify and bribe proposers, probably between 13.3min and 26.6min), I think the idea could help a little bit, improving the security for the justified (but not yet finalized) blocks.
The stake modifier could be transformed into something like:
kernelhash = hash(lastJustifiedBlock.snapshotHash ++ prevBlock.stakeModifier ++ utxo.time ++ utxo.hash ++ utxo.n ++ blockTime)
Regarding some concerns on how this change could make easier to do some grinding... I believe that the fact that we use an "old" block, that was already published, and voted by 2/3 of the validators, protects us against this kind of behavior.