Getting a transaction confirmed in Neptune Cash right now is hard. Broadcasting a ProofCollection
-backed transaction is easy enough, and you will probably be able to see it land in the mempool. If you are lucky someone else will Upgrade the ProofCollection
into a SingleProof
but if not then the transaction is destined to be ignored by the next block and all its successors. Even if someone does Upgrade the proof, there is still a high chance that the transaction does not make it into the next block. If that happens, it will most likely be kicked out of the mempool. In this case, your best bet is to try initiating the transaction again.
From a superficial reading, the difficulty of getting transactions confirmed is related anomalous behavior exhibited by the mempool. After all, the mempool should be a sort of staging area for transactions to be confirmed in the next block. One does not expect transactions to suddenly disappear from it for no good reason. However, putting the blame for this issue on the mempool risks misdiagnosing the root cause.
The heart of the mempool issue is an incentive problem. Upgrading transactions requires resources and nodes rationally prefer to devote those resources to tasks where the return is larger. Therefore, out-of-date transactions represent a burden and nodes clear their mempools of them. When it is time to compose a block, composers find their mempools empty.
There is insufficient bookkeeping. A node that upgraded a transaction in the past would have collected a fee for that job. Therefore that node has the incentive to keep that transaction up to date so that it can eventually be absorbed into a block. Presently, there is no code for keeping track of fees bound to the node’s own wallet for mempool transactions. Adding this mechanism would go a long way towards addressing the problem. And while I support adding that mechanism, I think it is treating a symptom and not the cause. Keeping track of collected fees does not present a compelling answer to the question why it is worth collecting those fees in the first place – particularly, given the other potential applications of the requisite proving power.
From a more profound point of view, the issue stems from resource contention. It requires proving power to Upgrade transactions and it requires proving power to produce block proposals. The same proving power cannot be applied towards both tasks simultaneously, and therefore it has to choose. Presently it is more profitable to produce block proposals, so that’s what provers end up doing.
In principle higher fees could solve this issue. However, the present code architecture, the configuration options, and the understanding on the part of users, are inadequate to regulate this market. Users cannot specialize in one of both proof-heavy tasks (Composing and Upgrading). Those tasks aren’t even cleanly separated in the code. So users end up having to do both, and rationally prefer to sacrifice the quality of whichever task is cheaper to sacrifice in order to maximize profit off the other.
The protocol stipulates that the Composer must to perform at least one Merge (in order to set the merge bit); this dynamic incentivizes him to confirm some transaction as opposed to none, which is good. But the Composer has no incentive to perform more than one Merge: the resources spent on the second Merge step are better spent outbidding competing Composers.
I propose the following perspective, and matching architectural and configuration changes.
- Block production consists of three steps, not two. The three steps are upgrading, composing, and guessing. Going forward, we should characterize upgrading as the first step in a three-step mining process.
- The Upgrader upgrades transactions (including Mergers) and gobbles transaction fees in the process. His task is to provide composers with ready-to-mine transactions. Subject to meeting a minimum proving speed, the composer benefits from having a larger proof throughput: the more transactions are merged, the more fees are collected. Any fees not claimed by the Upgrader are left to the remaining parties of the block production pipeline. The Upgrader is paid from transaction fees, but if there is enough competition he must cede a larger portion of that fee to improve the chances of his transactions being selected by a Composer.
- The Composer merges a transaction that has a coinbase with a transaction prepared by the Upgrader. The Composer proceeds to use this merge output as the basis for a block proposal. Any fees not claimed by the composer are left to the Guesser. Note that the fees now include the block subsidy. Contrary to the Upgrader, the Composer stands to benefit from faster proving and cares little about proof throughput: the faster the block proposal is produced, the sooner guessing can start, or the faster the competing block proposal can be outbid. The Composer is paid from both the block subsidy and the transaction fee (the proportion that was not claimed by the Upgrader), but if there is enough competition then he must cede a larger portion of that reward to the Guesser in order to incentivize Guessers to adopt his proposal.
- The Guesser contributes a difficult-to-find nonce to a block proposal (thus turning it into a block) and collects all fees not already claimed earlier in the pipeline.
- Just as Composers compete against each other with more profitable block proposals, so too do Upgraders compete against each other with more profitable aggregated transactions. The objective of the Upgrader is to ensure that at any point in time, the most profitable (for the next stages of block production) synced transaction in circulation is one he collects fees from.
Concrete changes proposed:
- CLI arguments:
--upgrade
configures the node to upgrade transactions (by default this mode should be disabled).--min-upgrade-fee
rename from--min-gobbling-fee
.--max-fee-left-unclaimed
regulates how big the portion of the fee that is not claimed by the Upgrader can get before it becomes uneconomical to perform upgrades – or phrased differently: defines what “economical” means.
- Separate mine loop into compose loop and guess loop; and add upgrade loop. Only one loop can be active at any one point. (Want to run more than one? Do it on multiple machines.)
- Upgrade loop:
- Upgrade any transactions that makes economical sense (this includes performing Mergers), and broadcasts the result. There is a discussion to be had about how to select Upgrade jobs. Optimizing for profit is a poor strategy because competing Upgraders might do the same. Probably a good idea to strike a balance between profitability and randomness.
- Record fees gobbled for upgraded transactions. Transactions with greater benefit to the node’s own wallet should be prioritized over others.
- Track the most profitable (from the perspective of the rest of the block production pipeline) synced transaction in circulation and, if possible, outbid it.
- When initiating transactions, default to
ProofCollection
plus fee.
How do these changes address the issue(s)?
In the abstract, we no longer depend on Composers to be altruistic. Instead, we depend on both Composers and Upgraders to be self-serving in a marketplace whose dynamics ultimately serve the user.
Concretely, the present state of the code has two limitations.
- There is no mechanism for merging transactions, except as a step in the Compose loop. Rational Composers will skip this step. For all we know, they already do. As a result, the effective throughput capacity of the blockchain is one (base) transaction per block. With this proposal, it becomes economical to run Upgraders on machines that cannot Compose competitively. The Upgraders collect fees by merging transactions, optimizing for throughput (because throughput, not time, maximizes fees). Ultimately the throughput capacity is limited by the block size and the number of Upgraders on the network – and independent from the target block interval. (Let that sink in.)
- There is no mechanism for keeping mempool transactions updated across blocks that do not confirm them (or conflict with them), unless those transactions were initiated by the node themself. Specifically, even nodes that upgrade the transactions of others – for example by transforming a
ProofCollection
into aSingleProof
and take a fee for that task – fail to record that they benefit from that transaction’s confirmation. Consequently, if a new block is found that does not confirm such a transaction (or conflict with it), then the induced Update task is (incorrectly) determined to be unprofitable and the transaction in question is kicked out of the mempool. Consequently, nodes broadcastingProofCollection
and charging the network with upgrading it have a small and random time window for success. Too early, and they don’t know the state of the mutator set to sync the transaction to. Too late, and a new block is found before the transaction’s upgrade and dissemination was complete. And even if they hit the success window, then they run into problem (1). According to the proposed perspective, Upgraders obviously need to keep track of which mempool transactions they already collected a fee from. Failure to keep track of this info is no longer a detail whose absence can be ignored; it is instead a jarring deficiency because collecting fees from transactions is the quintessential feature of Upgraders. With the shift in perspective comes the mandate to keep track of already-collected-fee already. With that deficiency remedied, Upgraders will Update, Merge and rebroadcast fee-paying transactions that were Upgraded fromProofCollection
toSingleProof
.