ibc icon indicating copy to clipboard operation
ibc copied to clipboard

ICS29: General Relayer Fee Protocol

Open AdityaSripal opened this issue 3 years ago • 38 comments

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.

AdityaSripal avatar May 26 '21 16:05 AdityaSripal

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 ibc-fee that will handle incentivizing relayers.

This ibc-fee module can implement a separate message:

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 PayFee callback in OnAckPacket and OnTimeoutPacket.

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 ibc-fee Keeper is in charge of paying out the relevant relayers and refunding any unused fees. This will allow any ibc app to be incentivized so long as the sending chain has some notion of fungible tokens, without introducing the concept into core IBC. Blockchains that have no concept of fungible tokens will need some other method to incentivize relayers.

There will need to be some channel registration so fees aren't sent to channels that do not support incentivization.

The above IBCFeeMsg explicitly includes a sequence, which is annoying for ICS-20 since sequence is not known in advance of sending transfer message. Rather than requiring users to send 2 separate messages, we could create a special fee msg that just incentivizes the latest sequence on specified channel. This allows users to send Transfer msg and incentivize it in a single MultiMsg Tx.

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.

AdityaSripal avatar May 26 '21 16:05 AdityaSripal

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.

ethanfrey avatar May 26 '21 17:05 ethanfrey

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.

ethanfrey avatar May 26 '21 17:05 ethanfrey

Some thoughts/questions:

  • how does refunding work?
  • how does receiveFee work if this is paid on a single chain?
  • why does fee need to be a separate message? Why can't we just wrap the existing TransferMsg with the fee logic?
  • as a user, I'm willing to pay a higher fee for a failed ack and a lower fee for a successful ack. Fail/success is not natural in the general case since acks can be anything, but I think it could be worth while just adding another field if we already specify receive, ack, and timeout (ie receive, failAck, successAck, timeout)

Food for thought: What about using ICS27 to allow a different denom to be paid on the receiving chain? I think this is an interesting problem worth discussion. Probably not the solution for a generalized fee? But it is certainly a problem that will reoccur. I want to initiate an IBC send on chainA and use an account on chainB to pay a fee to someone on chainB. This requires associating an account on chainA with an account on chainB

colin-axner avatar May 27 '21 09:05 colin-axner

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:

  1. The receiving chain does not need to be aware of the fees at all. This fee info thus does not need to be stored in the on-the-wire packet, but is an optional extension that can be stored separately from the packet by the sender ibc implementation
  2. The only info needed to be added to the ibc spec to allow this to be calculated on the sending chain is the address that submitted the packet on the remote chain. This could be passed back along with the acknowlegdement (or changing the acknowledgement data structure)

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.

ethanfrey avatar May 27 '21 10:05 ethanfrey

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 cosmos17fzz8v57p4uajhn2peysjgavyvveky2vnpemg6 successfully submitted the receive packet on the Cosmos Hub (thank you).

The address wasm10tsncqntt778xpkt7d5uwhul2rcmlj5c3larlj later submits an ack on the CosmWasm chain, and along with the ack, the info of the remote signer is included cosmos17fzz8v57p4uajhn2peysjgavyvveky2vnpemg6 in some provable manner. How do I pay cosmos17fzz8v57p4uajhn2peysjgavyvveky2vnpemg6?

  • I cannot use bank.MsgSend there as that is an invalid address
  • If I send the tokens over ics20 to the remote chain, I just create an infinite loop, using one packet to pay the fees of the last one, and this next packet must be incentivized itself
  • We could maintain some lookup, so I know that cosmos17fzz8v57p4uajhn2peysjgavyvveky2vnpemg6 on cosmoshub-4 is the same as wasm10tsncqntt778xpkt7d5uwhul2rcmlj5c3larlj on CosmWasm chain, but this is hard to prove (anyone else can claim they are cosmos17fzz8v57p4uajhn2peysjgavyvveky2vnpemg6 to claim fees), requires more complex interchain coordination, and leaks information that maybe not everyone is happy to share.

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.

ethanfrey avatar May 27 '21 11:05 ethanfrey

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:

  1. In what cases (for remote execution fee payment in general) do we want to pay on the remote chain? And in what cases do we want to pay on the source chain?
  2. If we pay on the source, how do we pay the address that did the work on the remote
  3. If we pay on the remote, how do we know what address to pay the fee?

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

colin-axner avatar May 27 '21 11:05 colin-axner

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.

milosevic avatar May 27 '21 11:05 milosevic

The missing piece is incentivising relayer to pass receive packet.

Which is actually the key point to incentivize in my mind. Otherwise, you just pay relayers to pass back timeouts

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.

This would simplify the issue, but this solution may well get is in legal problems. One of the requirements I took with the ics20-2 proposal is that anyone can relay any packet and get paid. Unless we hear differently in a legal opinion, let's keep this requirement in all designs.

ethanfrey avatar May 27 '21 14:05 ethanfrey

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 OnAckPacket and OnTimeoutPacket. This is a benefit of this approach since the ibc-fee message and the ibc-fee handler could vary completely from chain to chain without a problem. Maybe some chains will prioritize UX and just ask for a single fee that gets split up in a default way, others could provide a lot more flexibility. We should come up with a general default implementation, but we do not need to enforce a way to handle this info for all chains

The ibc-fee incentivization is orthogonal to core IBC, and only requires that apps provide the correct information on who relayed what.

The relayer has a different address on each chain and there is no guaranteed correlation between them.

This is a great point, thank you for pointing it out.

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. This would simplify the issue, but this solution may well get is in legal problems. One of the requirements I took with the ics20-2 proposal is that anyone can relay any packet and get paid. Unless we hear differently in a legal opinion, let's keep this requirement in all designs.

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 payto field in MsgReceivePacket

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 payTo address into the MsgReceivePacket, in the same way that they fill in the signer information.

When the receiving chain writes the ack, they just need to take the payToAddress and put it in the ACK. They don't need to understand anything about it other than that its a string. The payToAddress will be a valid address on the sender chain that can be paid out.

This incentivizes relaying the original packet, which as you said is the most important part of packet flow to incentivize

AdityaSripal avatar May 27 '21 15:05 AdityaSripal

how does refunding work?

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 ibc-fee msg handler.

When OnAckPacket gets called: ReceiveRelayer gets recv fee, ack relayer gets ack fee, timeout fee gets sent back to original incentivizer

When OnTimeoutPacket gets called: Timeout fee gets sent to timeout relayer Recv fee and ack fee get sent back to original incentivizer

how does receiveFee work if this is paid on a single chain?

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.

why does fee need to be a separate message? Why can't we just wrap the existing TransferMsg with the fee logic?

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. By keeping it a separate message you preserve very similar UX through multimsg tx, but you gain modularity/separation of concerns/flexibility. It's a similar reasoning for why fee information are not hardcoded into sdk.Msg but are part of superset sdk.Tx.

as a user, I'm willing to pay a higher fee for a failed ack and a lower fee for a successful ack. Fail/success is not natural in the general case since acks can be anything, but I think it could be worth while just adding another field if we already specify receive, ack, and timeout (ie receive, failAck, successAck, timeout)

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.

What about using ICS27 to allow a different denom to be paid on the receiving chain? I think this is an interesting problem worth discussion. Probably not the solution for a generalized fee? But it is certainly a problem that will reoccur. I want to initiate an IBC send on chainA and use an account on chainB to pay a fee to someone on chainB. This requires associating an account on chainA with an account on chainB

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 payTo address of remote relayer in its ack.

AdityaSripal avatar May 27 '21 15:05 AdityaSripal

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.

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.

AdityaSripal avatar May 27 '21 15:05 AdityaSripal

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. :100:

ethanfrey avatar May 27 '21 16:05 ethanfrey

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?

milosevic avatar May 27 '21 16:05 milosevic

Trying to summarize again...

  • All fee escrow/payment logic is done in the source chain - this is implementation specific but does not need to be in the ibc spec (discuss in detail in ibc-go)
  • This fee info per packet should be easily available to the relayers (which have chain-aware implementations), so they know what incentive they can expect for their services
  • The relayer should submit their source-chain address along with the IbcReceiveMsg in a chain-aware format
  • The acknowledgement committed on the destination chain must encode this payToOnSource address

So, most of the implementation discussion can go into ibc-go and focus on how the cosmos-sdk will handle it. The only part that will need agreement in the IBC standard is the last point: "The acknowledgement committed on the destination chain must encode this payToOnSource address"

Is this correct?

ethanfrey avatar May 27 '21 16:05 ethanfrey

If so, I see two potential ways to do this:

  1. every ICS application spec will need to pass this field in their app-specific acknowledgement messages. This must be passed both in success and error cases, so we should provide a different standard acknowledgement envelope that also includes this field. Any app using that will be able to pass the fee payer info into lower levels.
  2. We encode this when writing every acknowledgement. This means the data is always there on the ICS core layer, so the actual fee handling can be done one level below the application.

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.

ethanfrey avatar May 27 '21 16:05 ethanfrey

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)

ethanfrey avatar May 27 '21 16:05 ethanfrey

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:

  1. chain A puts together a fund to tip deliveries of successful transfer to/from chain A's bank from [chain B]
  2. dapps can reward deliveries of successful transactions (not spam)
  3. a DEX can pay different fees for SELL orders and BUY orders, depending on market conditions
  4. an individual can pay for delivery of their messages

A few key requirements:

  1. Neither the sender nor receiver select the relayer; relayers are still permissionless
  2. Fees can be paid without the receiving dapps participation (e.g., by a 3rd party)
  3. Dapps can participate in paying fees
  4. This does not require additional packets per packet. Some scenarios may require additional transactions (e.g., an occasional roll-up payment for accumulated fees), but nothind of the same cardinality

dtribble avatar May 27 '21 18:05 dtribble

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.

  1. when a relayer relays a packet, it adds a "delivery tips" address for the destination chain as part of its submission. Presumably that's in the packet, but could be separate metadata in IBC.
  2. the recipient module can query for the tips address and send it a payment (locally, on chain, with no IBC required)
  3. an external module can watch events and pay fees based on packets delivered. This keeps it out of the dapp code. Multiple payors including the dapp can contribute fees
  4. aggregate transaction data could be accumulated for off-chain processes to figure out who is owed what, and then airdrop fees. This is of course lower assurance that payment happens, but allows complex payment arrangement or computation (e.g., relayers get paid at the completion of a long transaction sequence, based on its success and timeliness).
    1. payments of tips can be aggregated for efficiency/convenience; e.g., collecting how many successful deliveries and then paying fees base d

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.

dtribble avatar May 27 '21 18:05 dtribble

So the things I don't understand yet:

  • All fee escrow/payment logic is done in the source chain - this is implementation specific but does not need to be in the ibc spec (discuss in detail in ibc-go)

I would expect that, in a send/ack pair, the destination chain would tip the send and the source chain would tip the ack. In the simplest case, they have nothing to do with each other, and each chain is just tipping all deliveries between chain A and B out of a "pseudo-altruism" fund to e.g., support connectivity between the chains.

  • how does refunding work?

I don't understand the issue around refunds at all. Why is this relevant here?

  • This fee info per packet should be easily available to the relayers (which have chain-aware implementations), so they know what incentive they can expect for their services

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.

dtribble avatar May 27 '21 18:05 dtribble

Trying to summarize again...

  • All fee escrow/payment logic is done in the source chain - this is implementation specific but does not need to be in the ibc spec (discuss in detail in ibc-go)
  • This fee info per packet should be easily available to the relayers (which have chain-aware implementations), so they know what incentive they can expect for their services
  • The relayer should submit their source-chain address along with the IbcReceiveMsg in a chain-aware format
  • The acknowledgement committed on the destination chain must encode this payToOnSource address

So, most of the implementation discussion can go into ibc-go and focus on how the cosmos-sdk will handle it. The only part that will need agreement in the IBC standard is the last point: "The acknowledgement committed on the destination chain must encode this payToOnSource address"

Is this correct?

This is all correct.

AdityaSripal avatar May 27 '21 19:05 AdityaSripal

If so, I see two potential ways to do this:

  1. every ICS application spec will need to pass this field in their app-specific acknowledgement messages. This must be passed both in success and error cases, so we should provide a different standard acknowledgement envelope that also includes this field. Any app using that will be able to pass the fee payer info into lower levels.
  2. We encode this when writing every acknowledgement. This means the data is always there on the ICS core layer, so the actual fee handling can be done one level below the application.

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.

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 payTo address in the exact same way and they will be calling the ibc-fee callbacks in the exact same way. So it is better to lift this burden into core IBC.

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. This doesn't need to happen for MVP, we can stick to a default for all apps. But we should definitely design the feature to support this future extension.

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)

AdityaSripal avatar May 27 '21 20:05 AdityaSripal

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:

  1. chain A puts together a fund to tip deliveries of successful transfer to/from chain A's bank from [chain B]
  2. dapps can reward deliveries of successful transactions (not spam)
  3. a DEX can pay different fees for SELL orders and BUY orders, depending on market conditions
  4. an individual can pay for delivery of their messages

A few key requirements:

  1. Neither the sender nor receiver select the relayer; relayers are still permissionless
  2. Fees can be paid without the receiving dapps participation (e.g., by a 3rd party)
  3. Dapps can participate in paying fees
  4. This does not require additional packets per packet. Some scenarios may require additional transactions (e.g., an occasional roll-up payment for accumulated fees), but nothind of the same cardinality

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.

AdityaSripal avatar May 27 '21 20:05 AdityaSripal

I would expect that, in a send/ack pair, the destination chain would tip the send and the source chain would tip the ack. In the simplest case, they have nothing to do with each other, and each chain is just tipping all deliveries between chain A and B out of a "pseudo-altruism" fund to e.g., support connectivity between the chains.

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.

AdityaSripal avatar May 27 '21 21:05 AdityaSripal

A few questions (not sure I fully understand the proposal):

  • for the new IBCFeeMsg message, there is no need for receiveFee, right?

  • don't we also need to add the pay_to to the MsgAcknowledgment and also a proof? So, the flow would be something like:

    • chain A sends IBCFeeMsg(channel, ack_fee, timeout_fee) + IBCSendMsg (Packet)
    • relayer
      • gets send_packet event (like today)
      • gets new ibc_fee event (needs to be easy to correlate with the corresponding send_packet. Relayer should also be able to query this on start. Will this be again from query_tx() or will it be stored?
      • relays MsgReceive(packet, proofs, signer, pay_to) -> extra field, added by relayer, needs ICS04 and proto update
    • chain B recv_packet handler stores the pay_to along with the ack bytes (when write ack happens)
    • relayer
      • gets the write_acknowledment event. This will include pay_to in addition to the old attributes.
      • gets the proof_pay_to in addition to the old proof_ack
      • relays MsgAcknowledgment(packet, ack, proof_ack, pay_to, proof_pay_to) -> extra fields, needs ICS04, ICS24 and proto updates
    • chain A acknowledgment handler verifies proofs, calls fee module to pay the relayer's pay_to the ack_fee
  • to support backwards compatibility a new channel version is required. Relayer and ics04 handler need to support multiple versions. Or we create a new channel type?

  • for timeouts pay_to cannot be proven

  • ackFee - ideally set to cover the 2 Tx fees, the one of the remote chain is not easy to estimate

  • timeoutFee - ideally covers the Tx fee

  • for unordered channels a relayer may selectively relay packets

  • for ordered channels a relayer could wait for others to relay small fee sequences, or just relay hoping for higher fees in the next sequences, or delay relaying until high fee sequences show up.

  • there is no incentive to relay acknowledgment with pay_to not matching the relayer's account.

ancazamfir avatar May 28 '21 07:05 ancazamfir

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.

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 payToOnSource address to the ibc application, so it could theoretically pay directly from the dApp in a fully dynamic way, so Dean's use cases could be handled without future breaking changes. But I see them as orthogonal to a sender paying fees for their own packet, and app-specific so not requiring any further extensions to the core ibc protocol.

ethanfrey avatar May 28 '21 10:05 ethanfrey

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.

chain A sends IBCFeeMsg(channel, ack_fee, timeout_fee) + IBCSendMsg (Packet)

This is go implementation detail. I would just extend the IBCSendMsg, but again, this is after we have a clear spec.

relays MsgReceive(packet, proofs, signer, pay_to) -> extra field, added by relayer, needs ICS04 and proto update

This field doesn't need to be in the packet and just needs to be in the go implementation IBCMsgReceive, thus I do not expect a need for ICS04 update, just ibc-go.

for timeouts pay_to cannot be proven

Which is good. No one submitted anything on the remote chain, so no one should get paid for that.

there is no incentive to relay acknowledgment with pay_to not matching the relayer's account.

There is. pay_to in the acknowledgement will determine which account gets the receiveFee for submitting the packet on the destination chain. Whoever submits the acknowledgement will get the ackFee. These may be different parties (but obviously the pay_to address has more incentive.

ethanfrey avatar May 28 '21 11:05 ethanfrey

chain B recv_packet handler stores the pay_to along with the ack bytes (when write ack happens)

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 acknoweldgement []byte with acknowledgement Acknowledgement and make this a struct like Packet. Packet has one field containing the application data along with metadata. The acknowledgement is currently only the application data with no way to add metadata. Something like:

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).

ethanfrey avatar May 28 '21 11:05 ethanfrey

.. relays MsgReceive(packet, proofs, signer, pay_to) -..

This field doesn't need to be in the packet and just needs to be in the go implementation IBCMsgReceive, thus I do not expect a need for ICS04 update, just ibc-go.

pay_to is not in the Packet itself but it is a new field in the MsgReceive which is standardized by ICS04 and protobufs

there is no incentive to relay acknowledgment with pay_to not matching the relayer's account.

There is. pay_to in the acknowledgement will determine which account gets the receiveFee for submitting the packet on the destination chain. Whoever submits the acknowledgement will get the ackFee. These may be different parties (but obviously the pay_to address has more incentive.

aah, so the ackFee goes to the signer of the MsgAcknowledgment and receiveFee goes to the pay_to. Missed this one. Thanks @ethanfrey

ancazamfir avatar May 28 '21 12:05 ancazamfir

chain B recv_packet handler stores the pay_to along with the ack bytes (when write ack happens)

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 acknoweldgement []byte with acknowledgement Acknowledgement and make this a struct like Packet. Packet has one field containing the application data along with metadata. The acknowledgement is currently only the application data with no way to add metadata. Something like:

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).

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 ack []byte talk to a chain with ack Acknowledgement?

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.

I would like to open a PR (likely monday) explaining how I would extend this.

Great! I look forward to reviewing it, feel free to address the above in your PR

AdityaSripal avatar May 28 '21 14:05 AdityaSripal