PPLNS with job declaration

I’m a bit worried about miners proposing fake block templates with absurdly high fees, thereby enjoying a relatively large payout.

This seems worse than block withholding, because such a miner could run a way with ~100% of the block reward with ~0% of the PoW.

The obvious counter measure is for the pool (or separate Job Declarator server entity, JDS) to verify every template. But this a non-trivial task, since the JDS node mempool could be very different. It may need to replace transactions its mempool with that of the template to check that it doesn’t contain a big fee transaction that’s actually unspendable.

Absurdly high fees would always trigger a new slice, so you can at least prioritize its verification.

For coinbase-only templates the JDS doesn’t know the transactions and can’t verify anything.

It’s also unclear how, in the random sample verification protocol, you would distinguish a malicious miner from a malicious pool making such templates for themselves (and ‘accidentally’ approving them).

Perhaps a simpler solution is to cap the fees for all slices to whatever fees were in the found block. The fake templates, if not caught, would then have their fees reduced to a level that at least in principle they could have found. Then it’s not much worse than regular block witholding.

This also creates a disincentive to produce high value templates with ‘secret’ transactions. Template providers will want everyone else to have the good stuff in their mempool too.

This seems worse than block withholding, because such a miner could run a way with ~100% of the block reward with ~0% of the PoW.

If a miner is providing ~0% of the PoW, then he would get ~0% of blocks subsidy and ~0% of the block fees, because the fee-based score is calculated on top of the difficulty based score. Suppose that a slice contains the shares from ther k_1-th to the k_2-th. Suppose also that the fee and the difficulty score of the i-th share are f_i and \bar d_i. Then score_f(i) = \frac{\bar d_i f_i}{\sum_{j=k_1}^{k_2}\bar d_j f_j}. If \bar d_i \approx 0 then this score_f(i) \approx 0.

The easiest architecture is that each pool run its JDS. In this case, the JDS validates each share. In the SRI, the JDS runs a light mempool just for faster recognition of the transactions. If there is some unknown transaction during validating the shares, then it will be asked to the miner through “PovideMissingTransactions” message, as for Sv2 protocol.

For coinbase-only templates the JDS doesn’t know the transactions and can’t verify anything.

I understand that “coinbase-only templates” is a share with just the coinbase transaction, so it is an “empty weak block”. But then you refer at the transactions, so there is more than one. I do not understand, can you explain?

Perhaps a simpler solution is to cap the fees for all slices to whatever fees were in the found block.

This reflects the state of the mempool when the blocks is found. If a share is produced in a moment in which the MMEF (mempool maximum extractible fees) is much less then the fee found in the block, then the share is devalued, even though the miner is very good at maximizing fees. This is not fair IMO.

It’s also unclear how, in the random sample verification protocol, you would distinguish a malicious miner from a malicious pool making such templates for themselves (and ‘accidentally’ approving them).

I don’t quite understand what you mean. Perhaps is worth pointing out that one of the main concerns is when the pool is also a miner, so there are some incentives for the pool to pay more for the fees it produced. Since a shares are public and those which are not valid can be spotted easily, one possible way the pool can cheat is by reordering shares by putting them in a slice with reference job that a a lower fee. But it is not trivial, because the job of the share must be compatible with the reference job, and fixing it involves timestamps or pointers, but this is outside the scope of the article.

If the JDS is an independent service, then we must take into account the fact that can service for different pools. In this setting, I think that each pool must communicate the JDS the coinbase outputs and each miner must communicate the pool that it is working for. Apart from this, seems to work exactly as it currently working in the SRI.

Try the following numbers:

  • fake fees: 1 million BTC
  • shares: 0.0001% of the pool for the interval

So they could steal approximately 1 BTC.

I assume you mean each proposed template (DeclareMiningJob), not each share?

If there is some unknown transaction during validating the shares, then it will be asked to the miner through “PovideMissingTransactions” message, as for Sv2 protocol.

This takes time and bandwidth. And in order to verify if the new transaction is actually valid the JDS has to insert it into its mempool. And in order to do that it may need to evict other conflicting transactions first. Doing this for every DeclareMiningJob may not scale very well.

I’m referring to a recent proposal that allows a DeclareMiningJob message with just a merkle proof for the coinbase transaction, without revealing which transactions are in the block.

I can’t find the link to the actual propososal, just a reference to in from the SRI call minutes: Discord

coinbase_only Mode and DeclareMiningJob

  • Optionality for DeclareMiningJob : Its usage depends on the mode.
  • Flag Renaming: The flag REQUIRES_ASYNC_JOB_MINING in AllocateMiningJobToken.Success will be renamed to better reflect its purpose.
  • Dropping Synchronous Case: Only two modes will remain:
  • coinbase_only JD Mode: No DeclareMiningJob is sent.
  • template JD Mode: DeclareMiningJob is sent.

I think there is a misunderstanding here. The pool is supposed to verify all the shares/jobs, the miners only a random subset. So this attack is not possible.

not the most scalable solution but is the best trade off that we have been able to find, and a pool can support it.

this system do not work with coinbase only

1 Like

No, I understand that’s the intention of your proposal. I just don’t think verifying all jobs is realistic.

SRI doesn’t implement it yet, it only checks that all transactions are present. If the checks are incomplete there will always be some exploit. And if the checks are complete, it may be too easy to DoS the JDS.

why you say that is easy to DoS the JDS? Do you have some numbers?

You could send infinitely many block proposals to it that each take a long time validate. See Great Consensus Cleanup Revival The blocks can be invalid, so it’s a free attack.

Fortunately there’s at least two mitigations for this attack:

  1. Don’t validate anything until you have received some threshold worth of shares
  2. Don’t allow non-standard transactions in the template

Simply checking if all transactions are in the JDS mempool is not enough. There might be an unknown transaction. Those are fetched via an sv2 message, but the transactions that obtained this way could all be slow to validate. And in order to check them, e.g. to make sure they’re not spending high value fake coins, you need to insert them into the JDS mempool. But maybe there’s conflict in the pool, so you have to evict some other transactions.

The best way to fully check if the proposed block is valid is by having the node verify it just like any other block, but without checking the PoW. There’s currently no RPC method to do this, e.g. submitblock requires proof-of-work. It may be useful to introduce such a method though.

I’m assuming that you can do it faster. At demand we rebuilt the mempool. Another assumption is that user are authenticated (otherwise there are other ways to DoS the pool), so I can at least detect who is building unusual block. For unknown txs we check if the tx is spendable and do not have conflict with other txs in the proposed block this is enough.

about solution (1) a pool can implement it outside of this proposal, for example the pool can send some work (so we are sure that work is valid) and activate the job declaration protocol and this extension only after that downstream sent some shares.

solution (2) is as well an implementation details, in this proposal we are not saying if pool can or can not deny some txs, and which should be denied.

Personally I like more (1) then (2) since I think that (2) go against the goal of sv2

Here’s a proof-of-concept implantation of a checkblock RPC for Bitcoin Core:

It can be used with block templates, which don’t have any proof-of-work. But it also has multiplier argument that can be used to raise the target for weak blocks, inspired by @instagibbs work in Second Look at Weak Blocks (though a different use case).