Hey folks,
This is an RFC for a consensus-breaking change to the mining protocol in order to provide a minimum-bid PoX payout. If the aggregate PoX payout – defined as the sum of all block-commits’ payouts – does not meet a protocol-defined minimum value, then no sortition takes place in that Bitcoin block.
Background
The good news is that @friedger and @Babo discovered that F2Pool, currently the 3rd-largest Bitcoin miner, is also a Stacks miner. The bad news is that F2Pool, being a Bitcoin miner, currently ensures that their Stacks miner wins 100% of the time in the Bitcoin blocks they produce (about 14% of all Bitcoin blocks). Doing so is trivial: F2Pool simply ignores other Stacks miners’ block-commit transactions when creating a Bitcoin block, thereby ensuring that their Stacks miners’ block-commit is the only block-commit in that Bitcoin block. Moreover, F2Pool’s Stacks miner only mines in F2Pool-produced blocks.
F2Pool’s behavior is currently allowed by the Stacks consensus algorithm. Normally in Stacks, miners must mine for a few consecutive Bitcoin blocks in order to have a non-negligible chance of winning. This ramp-up period prevents opportunistic miners who also stack STX from only mining when their PoX addresses are up for reward. But in the event that a ramping-up miner is the only miner in that Bitcoin block, then even though they’d otherwise have a negligible chance of winning (1/(2^256) odds), the fact that their chance is still non-zero when they’re the only miner means that they’re guaranteed to win.
This is very cost-effective for F2Pool, and would be for any Bitcoin miner that did this. Their block-commit transactions pay a 0 sat/vbyte transaction fee, and their PoX payouts are a paltry 196 sats (well below the dust minimum). The only payment they make is the opportunity cost incurred by bumping the least-valuable Bitcoin transaction from their block and replacing it with their own block-commit. Given that a block-commit transaction is about 390 bytes, this means it costs F2Pool a few USD to produce a Stacks block worth over $1000 USD each time they mine a Bitcoin block.
This behavior was predicted earlier, and is actually a positive sign. It means that Stacks is now valuable enough that miners are starting to consider it as part of the Bitcoin MEV. It doesn’t hurt the Stacks chain, but if you’re Stacking, this sucks. Instead of paying Stackers BTC, F2Pool gets to keep their BTC while also winning Stacks blocks as often as they win Bitcoin blocks. This drives the Stacking APY down.
What to Do About It?
If we want to stop this, we’ll need to do another hard fork. Fortunately, the change is very small and can be done relatively quickly (before sBTC).
I think addressing this is important for two reasons. First, Stacking a very popular component of Stacks that has found product/market fit, and it would be a shame to see it destroyed by MEV. Second, Stacking plays a central role in sBTC by serving as the ongoing reward to sBTC wallet signers for being available to process sBTC unwrapping without charging the user a basis point fee.
To address this, I propose creating a minimum PoX bid function. If the aggregate PoX payout – that is, the sum of a Bitcoin block’s block-commit transactions – is less than the minimum bid, then no Stacks block wins. This way, F2Pool would be obliged to pay out an amount of BTC that is comparable to the total payouts from the other Stacks miners if it wants to win the Stacks block.
How would this work? The consensus rules would be need to be updated so that the system looks at the past B Bitcoin block’s aggregate PoX payouts, and if the current Bitcoin block’s aggregate PoX payout has less than F(B) satoshis, then there is no sortition.
What are B and F? B is a function of how much Bitcoin mining power we expect to participate in MEV. If it’s 100%, then this tactic is unworkable – we’d need to find some other way to set a minimum PoX payout. But if it’s 90%, then B would be at least 10. If it’s 95%, then B would be at least 20. As long as the window includes at least one honest Bitcoin block, then we have something to work with.
F is the function that takes the aggregate PoX payouts as input for each of the B blocks, and outputs the minimum PoX bid. We’d need to be careful about choosing F so that mundane events like Bitcoin flash blocks don’t needlessly punish honest Stacks miners. For example, a flash block can cause most but not all Stacks miners’ block-commits to get bumped to the next Bitcoin block, leaving behind only one or two valid block-commits.
This is where I can use your help. I don’t know what a good F value looks like right now, but I’m confident we can find one. I have all the block-commit data for the chain in a Google sheet. I’ve highlighted sortitions in red where the aggregate PoX payout was abnormally small, due to miner MEV. They’re all towards the bottom, but as you can see, they’re starting to pick up.
If you’re feeling ambitious, please make a copy of the sheet and propose values for F and B. We’d want F to ensure that F2Pool does not win any blocks, but other Stacks miners are not unfairly punished.
We’d love to hear from you!
Drop a comment below on what you think the values of F and B should be! Support your claims with charts and data. The change to the code is pretty small, so if we can find good values, we can ship a SIP quickly and get a hard-fork going to stop this MEV behavior.