Skip to content
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

SNO-612: Extend sendToken for Polkadot-native assets #927

Closed
wants to merge 12 commits into from

Conversation

doubledup
Copy link
Contributor

Stacking this on #926. I'm not sure about initiating burn within Assets, given the need to invoke an agent. Would it be more appropriate to burn in Gateway? (where we invoke agents & register Polkadot tokens) Should we rather move Polkadot token registration into Assets as well?

SNO-612

@codecov
Copy link

codecov bot commented Aug 16, 2023

Codecov Report

All modified and coverable lines are covered by tests ✅

❗ No coverage uploaded for pull request base (polkadot-assets-on-ethereum@4ba3e14). Click here to learn what that means.

❗ Current head b7e3511 differs from pull request most recent head 9a216bb. Consider uploading reports for the commit 9a216bb to get more accurate results

Additional details and impacted files
@@                      Coverage Diff                       @@
##             polkadot-assets-on-ethereum     #927   +/-   ##
==============================================================
  Coverage                               ?   72.43%           
==============================================================
  Files                                  ?       41           
  Lines                                  ?     1810           
  Branches                               ?       92           
==============================================================
  Hits                                   ?     1311           
  Misses                                 ?      473           
  Partials                               ?       26           
Flag Coverage Δ
solidity 60.62% <0.00%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Comment on lines 418 to 430
struct RegisterTokenParams {
/// @dev The ID of the agent to register the token for
bytes32 agentID;
/// @dev The name of the new token
string name;
/// @dev The short symbol of the new token
string symbol;
/// @dev The number of decimal places allowed
uint8 decimals;
}

// @dev Register a new fungible Polkadot token for an agent
function registerToken(bytes calldata data) external onlySelf {
CoreStorage.Layout storage $ = CoreStorage.layout();

RegisterTokenParams memory params = abi.decode(data, (RegisterTokenParams));

address agent = $.agents[params.agentID];
if (agent == address(0)) {
revert AgentDoesNotExist();
}

bytes memory call = abi.encodeCall(AgentExecutor.registerToken, (params.name, params.symbol, params.decimals));
bytes memory response = _invokeOnAgent(agent, call);

address token = address(bytes20(response));
$.ownerAgentIDs[token] = params.agentID;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The gateway already has an Command.AgentExecute command for sending arbitrary messages to Agents. Lets use that rather than adding special-purpose commands for higher-level application stuff.

I understand we do need to keep a token registry in the gateway. So you could make the agent call back into the gateway to update the registry.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, my mistake, your approach here is correct. My memory was foggy as I just got back from leave.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, yes, we should be reusing using 'Command.AgentExecute' instead of creating a new toplevel 'Command.RegisterToken'.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The call back into the gateway opens a door to attackers: they can change the agentID for any token. This won't let them control the token (as they're not the owner recorded in the token contract), but it can be used to grief legitimate owners by changing the token's agentID so that calls to the token fail.

So we'll need some kind of authorisation when updating the token registry. It might be enough to require that the token hasn't been registered before.

contracts/src/Assets.sol Outdated Show resolved Hide resolved
contracts/src/Assets.sol Outdated Show resolved Hide resolved
contracts/src/storage/CoreStorage.sol Show resolved Hide resolved
@@ -410,6 +415,35 @@ contract Gateway is IGateway, IInitializable {
_transferNativeFromAgent(agent, payable(params.recipient), params.amount);
}

struct RegisterTokenParams {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When registering a foreign token, these params also need to include a unique and stable ID for the original asset on the Polkadot side.

This ID will be used later on for Polkadot->Ethereum transfers, to lookup the address of the erc20 contract of the wrapped token.

On BridgeHub, I suggest reusing the code below to produce this ID.

let agent_id = T::AgentHashedDescription::convert_location(&location)

The (name, symbol, decimals) metadata can't be used for this purpose. That's just metadata and has no guarantee of uniqueness.

Copy link
Contributor

@yrong yrong Aug 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree! Arbitrary transact also depends on the mapping here(asset_id <-> location) should be a runtime storage somewhere to support dynamic fee, currently we only support DOT to be paid as the fee of BuyExecution.

@@ -412,13 +412,14 @@ contract Gateway is IGateway, IInitializable {
}

// @dev Register a new fungible Polkadot token for an agent
function setTokenOwnerAgentID(address token, bytes32 agentID) external {
function registerToken(bytes32 tokenID, address token, bytes32 agentID) external {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've thought of a way we can prevent malicious parachains from using Transact to call back into gateway via an agent.

In AgentExecutor.sol, we make sure the destination of the transact is not the gateway contract. If it the destination is the gateway, we disallow the transact.

What we still need for this registerToken is a guard to ensure it's only being called by a registered agent. We may need to introduce another storage field which allows to perform this lookup. Unfortunately the storage field below only allows us to lookup by agentID:

mapping(bytes32 agentID => address) agents;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We receive the relevant agentID as the 3rd parameter here, so we can check that msg.sender == agents[agentID]. This means that only a registered agent can call registerToken and that they must register tokens for themselves. It's slightly stricter than just "registered agents only", but seems reasonable as the intention is for agents to register their own tokens anyway.

Base automatically changed from david/sno-611 to polkadot-assets-on-ethereum August 24, 2023 15:41
@yrong yrong mentioned this pull request Aug 25, 2023
@claravanstaden
Copy link
Contributor

Closing, will reopen if it becomes relevant.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants