-
Notifications
You must be signed in to change notification settings - Fork 992
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
Support setting a shielded address as refund target #4233
base: main
Are you sure you want to change the base?
Conversation
Co-authored-by: Jacob Turner <[email protected]>
Co-authored-by: Jacob Turner <[email protected]>
Co-authored-by: Jacob Turner <[email protected]>
Co-authored-by: Jacob Turner <[email protected]>
Co-authored-by: Jacob Turner <[email protected]> Co-authored-by: Yuji Ito <[email protected]>
Co-authored-by: Jacob Turner <[email protected]>
bbd65d4
to
9ced974
Compare
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #4233 +/- ##
==========================================
- Coverage 74.59% 74.40% -0.19%
==========================================
Files 342 344 +2
Lines 108724 109587 +863
==========================================
+ Hits 81101 81543 +442
- Misses 27623 28044 +421 ☔ View full report in Codecov by Sentry. |
@@ -3218,3 +3288,29 @@ fn packet_forward_memo( | |||
}) | |||
.expect("Test failed") | |||
} | |||
|
|||
fn gen_refund_target(test: &Test, alias: &str) -> Result<()> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should really be a disposable address but we don't have the right support for it in the wallet. I'll add a note to #4040
@@ -104,7 +104,7 @@ impl Display for MaspTxId { | |||
Serialize, | |||
Deserialize, | |||
)] | |||
pub struct MaspEpoch(Epoch); | |||
pub struct MaspEpoch(pub Epoch); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This has been made public to extract the inner Epoch
when constructing the refund storage key. Can't we instead implement KeySeg
on MaspEpoch
? Otherwise could we come up with a getter to extract the inner epoch from here? I'd like to avoid exposing the inner type as the whole point of this type was to ensure safe operations on it
MsgEnvelope::Packet(PacketMsg::Ack(msg)) => { | ||
let refund_info = recv_info_from_packet(&msg.packet, true) | ||
.map_err(StorageError::new)?; | ||
accum = apply_refund_msg( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it ok to apply the refund message without checking if the ack is successful or not?
What happens if the shielded refund transaction fails? In the wasm code of the ibc transaction we don't explicitly handle this case. I tried looking at ICS20 but I couldn't find anything in terms of what the refund operation should guarantee or not |
transfer: None, | ||
refund_masp_tx: None, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A refund masp tx should only exist if a transfer
does too. This should be reflected in the layout of the MsgTransfer
struct. Right now you can represent a MsgTransfer
with a refund_masp_tx
but no transfer
.
@@ -104,7 +104,7 @@ impl Display for MaspTxId { | |||
Serialize, | |||
Deserialize, | |||
)] | |||
pub struct MaspEpoch(Epoch); | |||
pub struct MaspEpoch(pub Epoch); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm more in favor of adding an accessor method rather than making this public, to discourage building invalid instances of this type.
// Shielded refund only happens if MsgAcknowledgement or MsgTimeout is | ||
// successful |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this comment is a bit confusing. it should only happen in case a timeout is triggered, or we receive an ack failure
if let Some((_, (epoch, refund_masp_tx))) = | ||
msg.transfer.as_ref().zip(msg.refund_masp_tx) | ||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For instance, make transfer: Option<Either<MaspTransaction, (MaspTransaction, Refund)>>
, or Option<(MaspTransaction, Option<Refund>)>
fn try_get_refund_masp_tx<Transfer: BorshDeserialize>( | ||
storage: &S, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method (or another one) should delete the refund shielding tx from storage. The refund tx is only needed in storage until an ack/timeout is received.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In shielded sync, the shielding tx could be retrieved from the IBC transfer transaction. It can't apply immediately because we don't know if the transfer hasn't failed at that time.
I tried to retrieve the refund shielding tx from storage in the PR for now. (But I have to fix to delete it when the ack is successful.)
Is it possible to have a kind of pending state of the IBC shielded transfer in shielded sync?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if extract_masp_tx
only returned the MASP tx in case the IBC packet was a timeout or ack failure? This way shielded sync wouldn't attempt to apply notes that could be in a pending state. This doesn't require many changes to the client code, but it might require reworking your current node code a bit...
Oh but I guess that still wouldn't solve the original problem, we can't delete the note from storage since we need to fetch during shielded sync. Perhaps we should give up on storing the shielding tx in storage, and carry it in a memo instead?
let packet_data = serde_json::from_slice::<PacketData>( | ||
&msg.packet.data, | ||
) | ||
Some(IbcMessage::Envelope(envelope)) => match *envelope { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some(IbcMessage::Envelope(envelope)) => match *envelope { | |
// These events are emitted on the receiver | |
Some(IbcMessage::Envelope(envelope)) => match *envelope { |
// If there is a transfer to the IBC account, then deduplicate the | ||
// balance increase since we already account for it below |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure what is being duplicated. Could you clarify, please?
let token = convert_to_address(ibc_trace).into_storage_result()?; | ||
let delta = ValueSum::from_pair(token, amount); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could be moved inside the if-expr
below, since they are only used there
.refund_masp_tx( | ||
&msg.packet.port_id_on_a, | ||
&msg.packet.chan_id_on_a, | ||
msg.packet.seq_on_a, | ||
masp_epoch, | ||
) | ||
.map_err(|e| Error::Context(Box::new(e)))? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
perhaps we could delete the refund tx when we call refund_masp_tx
packet.seq_on_a, | ||
masp_epoch, | ||
); | ||
if !storage.has_key(&refund_masp_tx_key).ok()? { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One note on this: a masp epoch change will trigger a failure of the shielded refund tx only if any of the involved assets is part of the conversion tree. If none of them is, then the tx will succeed at any epoch (provided that the assets have not entered the conversion tree in the meantime): so maybe we should try the shielded refund anyway before defaulting to the the transparent address (or maybe provide the MasEpoch
field as an option and always try when None
).
An additional note, the masp Transaction
could also carry an expiration in the form of a block height: this could cause a rejection by the MASP vp. We should double check how that is handled when the refund tx is created cause for obvious reasons it cannot match the expiration of the original unshielding tx
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we check if the assets have not entered the conversion tree in the transaction execution?
In the IBC logic, it just transfers(or mints) tokens from the IBC address to the MASP address. We can fall back the target to the transparent address if we know the shielded refund will fail at that time.
@@ -86,6 +98,8 @@ pub struct MsgNftTransfer<Transfer> { | |||
pub message: IbcMsgNftTransfer, | |||
/// Shieleded transfer for MASP transaction | |||
pub transfer: Option<Transfer>, | |||
/// MASP transaction for refund | |||
pub refund_masp_tx: Option<(MaspEpoch, MaspTransaction)>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here is where we probably want to remove the epoch or at least make it optional
@@ -36,6 +37,8 @@ pub struct MsgTransfer<Transfer> { | |||
pub message: IbcMsgTransfer, | |||
/// Shieleded transfer for MASP transaction | |||
pub transfer: Option<Transfer>, | |||
/// MASP transaction for refund | |||
pub refund_masp_tx: Option<(MaspEpoch, MaspTransaction)>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as below
context, | ||
masp_transfer_data, | ||
None, | ||
args.tx.expiration.to_datetime(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe we shouldn't use the same expiration we use for the transaction also for the refund tx. I don't see why anyone might want to set an expiration on a refunding tx so we could just avoid attaching an expiration at all in this case or we should try to come up with a clever way to estimate the delay we expect on the ibc route (if this is even possible)
@@ -2781,8 +2783,11 @@ pub async fn build_ibc_transfer( | |||
// The refund target should be given or created if the source is shielded. | |||
// Otherwise, the refund target should be None. | |||
assert!( | |||
(args.source.spending_key().is_some() && refund_target.is_some()) | |||
|| (args.source.address().is_some() && refund_target.is_none()) | |||
(args.source.spending_key().is_some() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we return an error instead if panicking in the sdk?
Describe your changes
Closes #4168
Based on #4134
Checklist before merging
breaking::
labelsnamada-docs
reponamada-indexer
ornamada-masp-indexer
, a corresponding PR is opened in that repo