-
Notifications
You must be signed in to change notification settings - Fork 391
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ICS29: General Relayer Fee Protocol #578
Comments
I have a rough sketch for a general idea that I want to pitch here before writing it up into a broader spec. I propose we still keep the notion of tokens away from core IBC, and implement a separate module called This type IBCFeeMsg struct {
string channelID
string portID
uint64 sequence
receiveFee sdk.Coin
ackFee sdk.Coin
timeoutFee sdk.Coin
} This message uniquely identifies the packet to be incentivized and specifies the fees to be paid for each potential action (receive/ack/timeout). Any IBC application can now "opt-in" to support incentivization by simply calling a func OnAcknowledgeent() {
// ...
// get receive relayer from acknowledgement. This only works if counterparty app supports incentivization
// if empty, refund receiveFee
// get ack relayer from message signer
ibcfeeKeeper.PayFee(channelID, portID, sequence, receive_relayer, ack_relayer)
} func OnTimeoutPacket() {
// ...
ibcfeekeeper.PayTimeoutFee(channelID, portID, sequence, timeout_relayer)
} The There will need to be some channel registration so fees aren't sent to channels that do not support incentivization. The above I think this is a relatively simple general fee solution that still avoids introducing fees into core IBC. Rather it allows opt-in by apps on chains with fungible tokens to incentivize their packets getting relayed through the ibc-fee module. |
Thank you for opening this issue, I think it will lead to a much deeper discussion on the fee issue. My first question is what is the desired behavior of how fees are paid out? It seems the user must pay all 3 fees when sending the packet. And will be refunded 1 or 2 of them. While configurable, it seems overly complex. |
My second comment is that it feels weird to jump into ibc-go implementation details here. Let us first discuss it on the level of spec and desired behavior. It should function for all possible chains, including solo machines. I would welcome a pr that captures much of this thinking on layer of spec so we can do line by line comments. |
Some thoughts/questions:
Food for thought: |
I would reiterate to have this as a spec-first design (no talk of ibc-go) and focus on desired properties like @colin-axner mentioned (where the fees go). My thoughts on minimal changes are that:
I also agree that this should only focus on packet incentivization. Client/Connection/Channel creation are 1-time events and will be paid by the person who cares about them. Packets may be created by many modules with no access to a relayer and are the key point to focus on incentives for. Furthermore, I would propose all fee storage/deduction to be handles in the ics4 channel/packet implementation and this information should never be exposed to the application logic. Either the app handles all fees in a custom way (like my proposal #577), or the ibc handlers handle all fees in a standard way (this proposal). But we should not mix responsibility between both apps and core ibc handlers - that can only create more error cases. |
Also, I realize there is one fundamental issue with this design. While it seems to make sense to pay the relayer on the source chain for work on the receiving chain, how can we do this? The relayer has a different address on each chain and there is no guaranteed correlation between them. Example: I set up a relayer between a CosmWasm chain and the cosmos hub. The address The address
This is a fundamental issue in the "pay ReceivePacket relayer as part of AcknowledgePacket flow" design. And let's discuss this theoretical point before looking any more into details. |
More generally, the issue is paying for execution/work done on a remote chain correct? I see the same problem occurring in a consumer <> service IBC app model. To step back a little, it does seem like doing all fee payments on a single chain is the ideal option, but in the general form of this problem, we can either pay for this remote execution on the source chain or receiving chain. So some questions to be answered:
I think it is possible that it is desirable to pay on source or remote depending on context. I also think it is possible to build a module/app that can handle both situations and handle relayer incentivization as a use case |
Thank you Aditya for opening an issue and coming up with a strawmen solution. I agree with Frey that having more detailed proposal would make it easier to point potential issues, but that takes time and it seems we can make some progress also at this level. There are several things I really like in this proposal: 1) it is nicely decoupled from core ibc , 2) it makes assumptions on existence of fungible tokens (incetivization layer) only on the sending side and 3) focus only on packets (assuming handshake is created by those that needs it). So my understanding is that paying fee would happen only as part of ack/timeout flow, i.e., a relayer that proves that packet is received or timed out gets fee on the sending side. This is nice and simple as relayer can include it's address as part of ack/timeout packet. The missing piece is incentivising relayer to pass receive packet. One idea is embedding relayer address on the sending chain as part of receive packet, so it can be added to packet acknowledgment, so no matter if different relayer sends ack, the relayer that sends receive packet will get paid. |
Which is actually the key point to incentivize in my mind. Otherwise, you just pay relayers to pass back timeouts
This would simplify the issue, but this solution may well get is in legal problems. One of the requirements I took with the |
Thank you all for the feedback. Definitely this should be written out as a spec. I've written it up in SDK terms just to demonstrate the general point, it's also the easiest way for me to writeup pseudocode while we still discuss at this level. It's also worth noting that the only things that needs to be consistent across implementations is how to provide the the address that submitted the packet on the remote chain and the callback interfaces used by The ibc-fee incentivization is orthogonal to core IBC, and only requires that apps provide the correct information on who relayed what.
This is a great point, thank you for pointing it out.
Correct me if I'm wrong, I believe you are misunderstanding Zarko's proposal. It's not the case that the relayer's address gets put into the packet by the user. But your point is solvable if we just add a MsgReceivePacket {
packet Packet
payToAddress string // this is the address of receivePacket relayer on source chain
signer string // this is the address of receivePacket relayer on dest chain
} Anyone can still relay the packet. The user is not making any choice here. The relayer themselves is adding their preferred When the receiving chain writes the ack, they just need to take the This incentivizes relaying the original packet, which as you said is the most important part of packet flow to incentivize |
This is up to ibc-fee handler and could vary from chain-to-chain. A default implementation would be that the sum total of fees get escrowed on When When
The address of the relayer who sent packet on remote chain gets sent inside the ack from the receiving chain. As noted above, this must be the relayer's address on sender chain.
We want it to be a separate message because it should work for all ibc-apps. We don't want to be hardcoding in the fee fields into every single ibc-app message. We also don't want individual ibc-apps to make assumptions about how the fee is structured. In my above example, I put in three separate fees. But another chain might choose one fee, or they may choose four (one for ack_success and one for ack_fail). If we hardcode it into application message, it will only be compatible with one type of ibc-fee. If it's a separate message, it will be compatible with any ibc-fee module that respects the same interface.
This is possible. Just a tradeoff between simplicity/flexibility. The nice thing is that different chains can choose whatever they want. Since fee handling is all on same chain, they don't need to agree on incentivization so long as they agree on how to communicate remote relayer.
I don't think we should rely on an IBC app for a general fee protocol. The general fee protocol should require as few assumptions about the two chains as possible. This proposal requires an assumption that sending chain has fungible tokens, and receiving chain will send back |
I think it only makes sense to pay on source since that is the only chain that can escrow. How would you pay everything on receiving chain without escrowing on sending chain in the first place? Once you've done that, you've added fee-handling logic in both places (and all underlying assumptions of fungible tokens) when you only needed it in one place. If you just handle everything on sender chain, then your ibc-fee protocol doesn't even use IBC in the first place which I think is a benefit. |
Aditya, thank you for pointing this out: MsgReceivePacket {
packet Packet
payToAddress string // this is the address of receivePacket relayer on source chain
signer string // this is the address of receivePacket relayer on dest chain
} I was understanding it was added to the Packet produced on the source chain. If the relayer itself adds it when processing receive, that would be much cleaner. 💯 |
This proposal looks very promising to me. I guess we need feedback from more people (@dtribble, @zmanian, @cwgoes, @charleenfei) and then we can potentially try to align/address potential concerns during next IBC call. And then if all is good, we can go after spec proposal. Does this make sense @ethanfrey? |
Trying to summarize again...
So, most of the implementation discussion can go into Is this correct? |
If so, I see two potential ways to do this:
To me (1) seems easier to slowly opt-in to for existing chains. (2) would be a bit harder to do in a backwards-compatible way with the current cosmos hub (but with some clever design it should be doable), but it will really make incentivization a first-class citizen. (2) is my preference, but it seems you were discussing (1) above. If this is this only thing that needs to change in core ibc, we should spend time to focus on that. |
I think part of my reservation of (1) and placing the fee refunding logic into the ibc application comes from writing ibc-enabled CosmWasm contracts. Forcing each of them to properly escrow and release fees, or providing them some sdk-callback to release the fees seems quite difficult/error-prone to me. I assume Agoric would have a similar situation, where they would like to decouple the ibc-speaking contracts from the fee payment. That said, if it is the preferred mechanism, I will figure out a way to support it. But if we assume a world where there will be dozens or 100s of ibc applications, we should really consider handling fees at a lower-level (like the cosmos-sdk ante handlers do rather than force every module to check and enforce proper fee payment) |
There were a few elements that I didn't understand, but this largely matches the approach I was considering. I'll post a few things separately to then try to clarify that. Example use cases:
A few key requirements:
|
To figure out the parts I don't understand, let me summarize the variant I was thinking about and then confirm that it's covered. Some variants may be infeasible even if the rest is feasible of course.
Because the recipient module can determine tips based on any application content, a DEX can for example pay a higher fee for delivering buy orders rather than sell orders when the market is urgently selling. For background, this was inspired by a proposal for incentivizing wallets: transactions submitted from a wallet include a "UI tips" field. Then any app or external process can send rewards to the wallet that led to valuable transactions. To be clear, my thoughts about the relaying have nothing to do with wallets; it was just an inspiration. |
So the things I don't understand yet:
I would expect that, in a
I don't understand the issue around refunds at all. Why is this relevant here?
This seems like it could be outside the scope of any of this. e.g., If Agoric and/or AiB sponsored traffic between the Agoric AMM and Gravity DEX, we could just say it, and folks would setup relayers and clean up (you know you would @jackzampolin). That's not incredibly robust but it sure covers a lot of scenarios for years. BTW I completely agree with Ethan's points about figuring our the right structure and flows before any technical details. |
This is all correct. |
Yes, I initially discussed (1) but I think (2) works just as well and offers some advantages. All ibc apps are going to be writing in the The one caveat is that we should definitely make it such that IBC apps can register their own custom ibc-fee module to be used. This way one app could use the default ibc-fee whose callback will require user to payout fee from escrowed funds. While another app could register a custom ibc-fee module that pays out fees from a commonly held fee pool. The only reason I see to support (1) long-term, is if we want ibc-apps to be able to explicitly disallow incentivization for their packets. I don't see any reason for this. Users who don't need incentivization can set 0 fees. Any fee they do set is additional incentive for relayer. App doesn't need to preemptively ban this. The other reason is as you said, (1) may be slightly easier to implement in a non-breaking way on a short time horizon. In the worst case, (1) could be a non-breaking MVP for an eventual solution that looks more like (2) |
Most of your use cases can be supported just by using a custom fee module. The one thing that isn't considered in the original proposal is the ability to condition fee payout on the type of packet you are sending. If you look at the callbacks I sketched out in original comment, they don't contain the packet. We could add the packet to the callback arguments. This would allow the ibc-fee module to use the packet as part of its logic when it does fee payout. So it isn't a problem technically. My two concerns are if there is any legal implications with paying fees based on packet type (especially the DEX example you posted above). The other is the relayer experience. Ideally the relayer knows exactly how much they will get paid before they relay the packet, and this won't hold in your DEX example if the fee is subject to changing market conditions. It's an interesting point of discussion that we should discuss at next IBC call. All of your key requirements are met. |
Hmm I think we're working with different canonical usecases in mind. My canonical usecase is a single user incentivizing their own packet flow, with pseudo-altruistic funds being a special case. These funds could still work. They would just be incentivizing packet flow coming out of their chain. So chainA could incentivize packets going to chainB, and incentivize acks/timeouts coming back. The proposal I have does not include a way to incentivize a relayed packet from the receiving chain. |
A few questions (not sure I fully understand the proposal):
|
I agree. We can pass the relevant info into the dApps to incentivize, but dynamic values are unlikely to be useful. These payments are only useful if relayers can easily and reliably determine how much they will receive for relaying a packet before they relay it. Ideally with <1 query per packet. I would support passing both the signer (packet submitted) and |
Thank you for your comments, Anka. I agree with most of this, but consider the go structs above a loose prototype, and I would propose different go implementations that also handle this as a decorator architecure like Aditya suggested above. As I mentioned here with Aditya's agreement, there is only one change needed to the IBC specs. I would like to just focus on that now and once we have agreement there start the go details.
This is go implementation detail. I would just extend the
This field doesn't need to be in the packet and just needs to be in the go implementation
Which is good. No one submitted anything on the remote chain, so no one should get paid for that.
There is. |
This is the key change and what affects the payloads and proofs between chains. I would like to open a PR (likely monday) explaining how I would extend this. In short, I would replace message Acknowledgement {
data bytes = 1;
payTo string = 2;
} Would be a start and would allow for future extensibility without such breaking changes (we can add an optional field to Packet already). |
aah, so the |
Thank you @ethanfrey for focusing the discussion on what needs to happen on ICS first. Do you intend for the above proposal to be backwards compatible? Can a chain with If not, is backwards compatibility a requirement for the short term incentivization protocol? I'm in support of this proposal if it matches the backwards-compatibility requirements from ecosystem.
Great! I look forward to reviewing it, feel free to address the above in your PR |
just read through this conversation, great breakdown. as far as i can see so far, the proposed route still allows the relayer to fill in its own address as regarding the changes to ics-04, i took a look per @ancazamfir 's comment and haven't seen any standardization of the receive message, just of the acknowledgement envelope which SHOULD return bytes but could return another Acknowledgement type potentially. maybe i missed it? |
In ICS04 |
Yes, we must update the functions in the spec. And the proto defs in ibc-go. I will start a strawman of this Monday or Tuesday to replace my ics20-2 proposal |
Thanks for the discussion all. Most of this discussion seems to assume that fee payment should happen on source chain because as the initiator of the packet that's the place where the user can actually specify funds to be escrowed to pay fees for the entire packet life cycle. But on the other hand, if possible, I think it would seem more natural for fees to be paid on whatever chain a tx was successfully committed on (ie. ack/timeout on the source chain and receive on the destination chain). And that way we wouldn't need to carry the destination chain relayer address back to the source chain through an extension to the Would it not be possible to leverage ICS20 itself for the fee payment for the Haven't thought through exact details yet. I guess the ICS20 transfer would be to the ibc fee module on the destination chain (note we'd have to enable transfer to this module address ...). Alternatively we could have a variant of ICS20 that's just for this use case, where the packet is intended to be relayed along with some other payload and just pays the relayer. In fact maybe the proposal here could be turned into this, where the new IBCFeeMsg effectively transfers the |
That was my first approach and easily allows payments on the remote chain, but only for ICS20. There was some decent discussion on that item and in the IBC call a desire to make a more generic solution. If we make a generic one, the only approach we could reason about was paying on the source chain when the receive/ack pair or timeout was complete. The problem with every other protocol is it could not just mint ics20 tokens that were escrowed somewhere else. So either every protocol becomes a superset of ics20-2 (allow it to transfer tokens along with other actions), or we pay on source chain. I was quite a proponent for just doing the simple ics20-2 solution and getting the hub incentivized. But seeing that a generic solution may be possible without a huge amount of coding, I am leaning towards this. Using one (ics20) packet to pay the fee for another packet brings up the issue of who pays for the ics20 packet? In the end, with this proposal, I would assume the relayer would just accumulate some amount of payment on the source chain(s) and after some large number, move those tokens onto the destination chains where it needs to top up it's account to pay fees (and can use ics20 once there to collect the fees for say 100 packets). Forcing us to transfer fees with a separate packet for every app packet seems much less scalable and less flexible than allowing the relayer to relay them as a batch when needed (same work to send 0.01 atom or 100 atom in one ics20 packet) |
It still seems to me that "delivery is paid for by receiver or receiver's fee keeper" supports all the scenarios. What are the specific scenarios that can't be supported with that? Sources can send ics-20 payments to top up their reserves on the destination chain., but that's a batch operation, not one-per-message. There could be a mechanism for asking about the prices for relayers. That's a nicely independent mechanism for posting or even dynamically negotiating prices.
OK I'll think more about this use case. The "pseudo-altruistic" approach btw is for near term to enable simple things to be rolled out quickly with some amount of funding getting the packets flowing. We need to get more precise control and incentives, but I think that can be longer term. The advantage of the simpler models is that they are easy and incentive-aligned to roll-out and fund.
I think these are non-issues, but of course I'm interested in legal opinion. Basically as you note:
So neither the receiver nor the sender are making a choice. The relayer is just an arms-length transmitter of opaque data blobs (and indeed protocols could perfectly well encrypt in the future). Whether -- like stateful packet inspection firewalls -- they inspect the blobs more closely than required in order to better do their jobs seems independent. |
This is a side note as it is an aspect of chain implementations of the ibc-fee, but the ibc-fee implementation should allow for adding to the existing fees for the packet (as opposed to setting it once). This helps in the cases:
Apologies if I'm restating something said above |
Just as an aside (which may already seem obvious to the people involved), for any ibc-go implementation, please ensure that the sender's Thanks! |
Closing this since ics29 spec has already been merged. |
Is there an ETA on an implementation being ready to use? Not just coded, but with relayer support and tested on some testnets at least. Or is this something ready now for any new chain to use, just requiring the channel upgrade for existing channels? |
It's ready for use in Hermes, to be released officially in v1.1 (before end of Oct). No testnets that I know of yet, only developer networks. Some useful testing notes
Should be ready for new chains to use. Gaia reported to us they will not enable fees until channel upgradeability is ready. I don't know of other networks. |
Fees aren't baked into IBC protocol because IBC does not have the concept of fungible tokens in-built to the protocol. This is an important and correct decision to make IBC as general as possible. IBC should still work with ledgers that have no tokens, and in contexts where a relayer fee isn't necessary.
However, we should still have a general approach to incentivizing application packets getting relayed. This issue is a place to collect discussion on possible approaches.
The text was updated successfully, but these errors were encountered: