Replies: 2 comments 6 replies
-
|
Thank you for writing this up!
I think there is an unfortunate problem we have with this, which is described in more detail in It could also be that I'm overstating the problem 😅, but in any case, I think good dry-run support would simplify a lot of things, including enabling very flexible fee estimation and payments as described in your post and good (meaning proportional to the tx) service fees (0xMiden/rust-sdk#1956). Maybe we should investigate the "signature dummy verification" approach. |
Beta Was this translation helpful? Give feedback.
-
|
Thank you for writing this up! I like the general direction but I think there are a few things to clarify. I haven't thought this through completely - but a few preliminary comments:
As @PhilippGackstatter mentioned, this would be highly non-trivial (especially in the context of network transactions) - but I think there are some workarounds that can make paying fees in the auth procedure possible (see #2899 (reply in thread)).
Making We also don't really need the
I'm not yet sure if the fees should be burned at the batch level of block level. The way I imagined this originally was that users pay fees to batch builders, and batch builders pay fees to block builders, and then the protocol-level fees are applied at the block level.
The pre- and post-delta may still be desirable. Or rather, it would be good to have a mechanism where users can authorize transactions before the fees are taken out. This is especially relevant in the context of multisig accounts: collecting signatures would happen asynchronously and we don't want to have the first signer commit to a specific fee because by the time the last signer signs, the fee requirements may be different. But I think we may be able to solve this at the standards level rather than at the protocol level.
Is the "one fee note" part something we want to enforce at the protocol level? It would be nice to do so - but not sure if this can be done easily. One other thing we should keep in mind is how this all going to work in network transactions. The auth procedure there is considerably simpler - so, fee estimation should be way more precise. But I think we'd still need to provide some thing like an |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Summary
We no longer remove
tx_feefrom the account vault during the transaction kernel, and instead only output fee computation parameters. The batch kernel sums the computed base fees across included txs (batch_fee= sum of alltx_fees), and requires that the batch kernel burns >=batch_fee.As part of
miden-standards, each tx further outputs a single erasable FeeNote carrying any asset of the user's choosing, and with the batch builder set as theNoteRecipient. This is the canonical way to pay fees, but users can get their tx included in the batch by non-standard channels if they wish, such as offchain payments to the builder.To support FeeNote we enhance (which is also independently useful), the current mechanism of erasable notes. Specifically, we add an optional
MustBeErasedmarker on output notes, which serves two purposes:This marker, combined with a simple analysis of batch builder incentives guarantees erasure of the FeeNote at the batch level.
End-to-end flow
tx_feeMustBeErased-marked FeeNote to their tx, payable to the batch builderbatch_fee, and burns itΣ FeeNote.asset_i - batch_feeThis proposal shares some design principles with the Flashbot bundles, except that it's defined at the protocol layer rather than the application layer.
Background
Today the tx kernel does most of the fee work:
compute_feederives the fee fromclkandverification_base_fee.compute_and_remove_feebuilds a fee asset and removes it from the account's vault as the last step; this means the fee is not part of the asset preservation checks.fee_faucet_id) is accepted.This shape forces a number of designs that wouldn't otherwise be needed:
Proposed mechanism
(protocol) Transaction kernel emits a fee summary
compute_and_remove_feegoes away, with onlycompute_feeleft, with the extra simplifications/modifications:compute_feecan be called at the end of the epiloguecompute_feedoesn't care aboutESTIMATED_AFTER_COMPUTE_FEE_CYCLESanymoretx_feeas:FEE_COMPUTATION_PARAMS = [tx_fee, clk_count, 0, 0](optionally, replace0, 0withexpected_output_notes, nullifiersfor explicit accounting)(standards) Every tx outputs one erasable fee note
The canonical way to pay fees becomes a
miden-standardsFeeNote with the batch builder as the designated recipient.From a protocol standpoint, the note carries whatever asset the user wants. This flexibility at the protocol level is tightened in practice, because ultimately it's the batch builder who will decide if they accept the transaction in their proposed batch. So the user and the batch builder enter a sort of implicit agreement, which can be as simple as:
Or more complex such as:
In any case, the user's tx includes a FeeNote carrying one of these assets with the expectation that the builder includes their tx. This naturally drives competition among batch builders.
The selection of the batch builder happens at the application level, e.g. via the wallet: The Miden Wallet would canonically route the requests to the "best" builder (by some heuristic, e.g. cheapest), with the optionality to manually select builders and how the transactions are communicated to them (e.g. standard mempool vs. private inclusion).
The
NoteRecipientis either a fixed builder account or a builder-flexible mechanism. See "Open Questions".(protocol) Batch kernel accounting
The batch kernel will include the following logic:
FEE_COMPUTATION_PARAMS.MustBeErasednotes are indeed erased.total_cycles = Σ clk_count_iover all included txs (erased & non-erased)total_output_notes= count of entries inOUTPUT_NOTES_SMT_ROOT(post-erasure)total_nullifiers= count of nullifier entries inINPUT_NOTES_COMMITMENT(post-erasure)batch_feeas:Notice that this cost will be greater or equal to the raw sum of
tx_fees, with the potential mismatch coming fromtotal_output_notes >= expected_output_notes(see footnote*).batch_feeamount. We don't yet have a mechanism for this, but it could be similar to today's removal of the fee from the vault - except lifted a layer up, from the transaction to the batch kernel. Another mechanism could be a special BatchBurn note.Σ assets collected from fee notes − batch_fee.(standards) Fee estimation
The user picks some fee asset they wish to pay the batch builder in, sets its value to some arbitrary amount (e.g.
FeeNote.asset.amount = 1), and dry-runs the tx to compute the realtx_feecost.Since the FeeNote is standardized, and assuming that moving an asset from the user's vault is amount-independent, this lets the user obtain the
tx_feeexactly, since nowcompute_feeruns at the very end of the epilogue.Now that the user knows the exact
tx_fee(in native fee asset) that the batch builder must burn as enforced by the batch kernel, they can set theFeeNote.asset.amount = convert(builder_id, tx_fee, FeeNote.asset_id).(application) Fee estimation
Now as mentioned above,
convertfunction may be builder dependent and is called at the application level. For example:This might be handled by the client and made very efficient by caching most of the parameters for the "default" choice of the builder.
(protocol) Pricing the FeeNote itself
The FeeNote has its own resource cost, which is taken into account in
compute_fee, but without special protocol-level provisions (it's a standard, so anyMustBeErasednote is priced like this). The FeeNote incurs:There is also zero cost per-nullifier for erased notes, but its paying tx would be the consumer, not the producer. So whether or not the note is nullified doesn't directly affect the cost of the tx that outputs this note.
So unlike the current mechanism for removing assets from vault after
compute_fee, this proposed mechanism is simpler and doesn't have privileged treatment.Why do we need all this?
Multi-asset fees fall out for free. The tx kernel never sees a fee asset, so there's nothing to be opinionated about. The user picks the asset and the builder accepts or rejects the transaction based on the FeeNote following the market dynamics. This subsumes #2551.
The tx kernel shrinks. We can simplify/remove/avoid/ a lot of logic such as:
compute_and_remove_feeInsufficientFeeerrorMulti-dimensional fees are natively part of the proposal This proposal assumes per-resource base fees (#1685) with cycle counts and at least one more dimension for number of output notes. With only a single fee like we have today, the design still works but foregoes the "erased notes are cheaper" win, since there's nothing to charge for those slots in the first place. I think the multi-dimensional fee mechanism fits very nicely with this proposal and we can almost get the multi-dimensionality for free.
User-defined batches handle their own fees. A user submitting their own batch can simplify things and completely avoid FeeNote, as long as the batch pays its required
batch_fee.Erased notes' savings get passed on to the user, which is only possible with a)
MustBeErased-marker, or; b) refunds. While a) doesn't address the problem for all erased notes, it covers most use cases (the batch might still erase some notes which the user didn't mark asMustBeErased).#2765 (auth doesn't bind fee) gets simpler. The tx kernel doesn't compute a fee, so there's no fee for a malicious prover to inflate. Cycle-griefing via tx-script swap is still a concern, but the consequence is bounded - the user paid for a fee note ahead of time at their estimated cost, and the inflated cycle count becomes the builder's "problem" to either reject or absorb (not really a problem, it will just be decided by the fee market).
Open questions
NoteRecipientof the FeeNote? Builder-flexible recipients are interesting in their own right and may deserve a separate discussion. Could we come up with a recipient that encodes "any account that owns the batch's coinbase note can claim this FeeNote"? It would couple nicely to the #1687 coinbase mechanism. Other options are "hardcoded to a single builder account?" AnOR(...)chain of accepted builders? The Flashbots-bundle analogy suggests the user should be able to target a specific builder; the public-good analogy suggests builder-flexible. Since this is intended to be an application-layer choice, we might be able to support multiple mechanisms here.Footnotes
*I wonder if we can skip this check in the batch kernel, because the batch-builder incentives would already soft-enforce this. Specifically, if the builder includes the tx with, but fails to erase, the
MustBeErased-note, then they will end up paying out-of-pocket for the difference between the tx-kernel-computed lowerexpected_output_notesand whatever the batch kernel actually computes, which istotal_output_notes.So first of all, the batch builder will want to collect the fee from FeeNote, but if for some reason they decide not to, they will actually be penalized for it.
Beta Was this translation helpful? Give feedback.
All reactions