-
Notifications
You must be signed in to change notification settings - Fork 721
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
Snowbridge v2 - Inbound Queue #6697
base: master
Are you sure you want to change the base?
Snowbridge v2 - Inbound Queue #6697
Conversation
Co-authored-by: Vincent Geddes <[email protected]>
Co-authored-by: Vincent Geddes <[email protected]>
Fix register token
|
||
let mut origin_location = Location::new(2, [GlobalConsensus(network)]); | ||
if message.origin == GatewayProxyAddress::get() { | ||
// If the message origin is the gateway proxy contract, set the origin to |
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.
You do not need this branch. The origin is already set here:
UniversalOrigin(GlobalConsensus(network)),
So you can only add AliasOrigin
if its not the gateway proxy.
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.
Changed in 02750d6.
type Balance = Balance; | ||
type MessageConverter = snowbridge_router_primitives::inbound::v2::MessageToXcm< | ||
EthereumNetwork, | ||
ConstU8<INBOUND_QUEUE_PALLET_INDEX>, |
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 pallet index is for the inbound queue v1 pallet. We are essentially re-using it for the v2 pallet and I am not sure if this is the right thing to do. We may need to add the v2 pallet index separately.
This is used to allow the use of the UniversalOrigin instruction, which only the v1 pallet is configured to use now.
Its set in xcm config here:
polkadot-sdk/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs
Lines 464 to 465 in 41a5d8e
type UniversalAliases = | |
(bridging::to_rococo::UniversalAliases, bridging::to_ethereum::UniversalAliases); |
And bridge config here:
polkadot-sdk/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs
Line 687 in 41a5d8e
(SiblingBridgeHubWithEthereumInboundQueueInstance::get(), GlobalConsensus(EthereumNetwork::get().into())), |
Basically it means only the bridge hub inbound queue v1 pallet can be the Universal origin { parents: 2, interior: X1([GlobalConsensus(Ethereum { chain_id: 11155111 }) }
(Snowbridge Sovereign)
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.
Addressed in 40e85ea.
@@ -12,6 +12,7 @@ workspace = true | |||
|
|||
[dependencies] | |||
hex-literal = { workspace = true, default-features = true } | |||
hex = { workspace = true, default-features = true } |
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 looks like a dev dependency only?
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 whole crate is tests only and then all dependencies should be under dev-dependencies
, I think.
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.
AH ok, ignore then.
Self::deposit_event(Event::MessageReceived { nonce: envelope.nonce, message_id }); | ||
|
||
// Set nonce flag to true | ||
Nonce::<T>::set(envelope.nonce.into()); |
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.
Not sure if re-entrancy is a potential security issue for substrate pallets like it is for smart contracts, but generally you want to increment (Or in this case set) the nonce first and then dispatch.
See this comment:
https://github.com/Snowfork/snowbridge/pull/1300/files#diff-74470ba8f35b42dbc8805739e92708c0696343e0b1819feb3e7a5ebb77cc623cR140-R143
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.
That's a good point, d70ac6c.
&mut message.xcm.as_ref(), | ||
) | ||
.map_err(|_| ConvertMessageError::InvalidVersionedXCM)?; | ||
message_xcm = versioned_xcm.try_into().map_err(|_| ConvertMessageError::InvalidXCM)?; |
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 possible alternative to failing here could be let the message go through with a empty xcm payload, which will essentially trap the funds on assethub, allowing the user to claim them. Lets discuss this.
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.
That would be more work for the user though. I think it's better to fail early and not move any tokens around than allowing it and the user needs to take corrective action. Unless you see a benefit to doing this?
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.
The funds are already locked on the Ethereum side so tokens are moved by this point. If the xcm is malformed for any reason there is no corrective step that can be applied here. The message is immutable and nothing can make the xcm well formed once submitted on the etherum side.
So the options for malformed xcm are:
- Return error and revert: Funds are trapped on ethereum, no way to push the message through. The message is undeliverable due to the revert.
- Push message to Asset Hub with empty xcm: The funds get trapped on asset hub where there is a claim process. If the user does want to do the extra step of claiming, then the funds are essentially still trapped, but on asset hub where it is claimable in some form.
- Maybe if the bug is in our code, we could apply a fix to the inbound-queue-v2 and go through the whole release process, and a message could be retried after the release is done.
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 see what you mean, thanks for explaining. I have changed it to allow invalid xcm: 6af760b
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 did the same for an invalid claimer payload: b368760 If the claimer bytes cannot be decoded into an Xcm junction, don't add the claimer to the Xcm. The relayer will then be set to the claimer, as you mentioned here: #6697 (comment)
What do you think about foreign ERC20 (FNB) tokens that cannot be converted into a asset ID? Should we error? Or not add to the assets vector?
AccountKey20 { network: None, key: (*token_id).into() }, | ||
], | ||
); | ||
instructions.push(ReserveAssetDeposited((token_location, *value).into())); |
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.
ReserveAssetDeposited
takes multiple Assets as an argument. Instead of using multiple ReserveAssetDeposited
with a single asset in each, do one ReserveAssetDeposited
with multiple assets.
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.
let asset_id = ConvertAssetId::convert(&token_id) | ||
.ok_or(ConvertMessageError::InvalidAsset)?; | ||
instructions.push(WithdrawAsset((asset_id, *value).into())); | ||
}, |
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 here with WithdrawAssets. Pass in multiple assets.
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.
} | ||
} | ||
|
||
if let Some(claimer) = message.claimer { |
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.
Set the claimer before ReserveDepositing or Withdrawing assets so that in the case of a failure, the user can at least claim some of their funds.
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.
origin_location | ||
.push_interior(AccountKey20 { key: message.origin.into(), network: None }) | ||
.map_err(|_| ConvertMessageError::InvalidOrigin)?; | ||
instructions.push(AliasOrigin(origin_location.into())); |
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.
We could potentially use DescendOrigin here instead of AliasOrigin. This is because AliasOrigin in this case is essentially doing a descend origin i.e. appending the msg.sender to the existing origin.
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.
Agreed, it is better. 9ffb3e1
// Refund excess fees to the relayer | ||
DepositAsset { | ||
assets: Wild(AllOf { id: AssetId(fee_asset.into()), fun: WildFungible }), | ||
beneficiary: origin_account_location, |
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.
We should maybe refund to the claimer if its present, and if its not than refund to the relayer. The relayer will get its reward separately.
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.
…ound-queue-v2-clean
# Conflicts: # bridges/snowbridge/primitives/router/src/inbound/mod.rs
Description
Implements the Inbound Queue for Snowbridge v2.
Integration
This PR adds:
EthereumInboundQueueV2
with asubmit
extrinsic.InboundQueueApiV2
with a methoddry_run
, that takes an Ethereum command and converts it to Xcm and provides the execution fee on BH and static fee part of the AH execution.Since this is a new pallet, no breaking changes are made to the
EthereumInboundQueue
pallet.Review Notes
The Inbound Queue v2 pallet expects an abi-encoded envelope from Ethereum. Once decoded, the payload parameter is SCALE decoded into a v2::Message. The message contains:
origin
: The origin address of the user on Ethereumassets
: A vector of assets that will be placed in the holding on AH. Can be a native ERC-20 or a foreign ERC-20 (Polkadot native assets). The XCM instructions that are used differ per asset type (ReserveAssetDeposited
for native ERC-20 tokens andWithdrawAsset
for Polkadot native assets). The user on Ethereum specifies additional xcm deposit the asset into a beneficiary account.xcm
: User provided xcms, to deposit the asset to a beneficiary, provide further fees or other instructions such asTransact
(for example, to register a token)claimer
: The claimer on AH, in case funds are trapped so it can be claimed.The relayer pays a DOT fee that covers:
The DOT fee is burnt from the relayer account and teleported to AH.
Messages may be processed out of order. Nonces are stored in a sparse bitmap for space-efficient storage.