-
Notifications
You must be signed in to change notification settings - Fork 137
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
NEP-366: Meta transactions #366
Changes from 5 commits
f08cf90
09bdb8e
7cf1baa
6c6720e
00cebb3
acd3fea
56ddd62
4d1ac73
003e589
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,148 @@ | ||||||||||
--- | ||||||||||
NEP: 366 | ||||||||||
Title: Meta Transactions | ||||||||||
Author: Illia Polosukhin <[email protected]> | ||||||||||
DiscussionsTo: https://github.com/nearprotocol/neps/pull/366 | ||||||||||
Status: Draft | ||||||||||
Type: Protocol Track | ||||||||||
Category: Runtime | ||||||||||
Created: 21-Jun-2022 | ||||||||||
--- | ||||||||||
|
||||||||||
## Summary | ||||||||||
|
||||||||||
In-protocol meta transactions allowing for third-party account to initiate and pay transaction fees on behalf of the account. | ||||||||||
|
||||||||||
## Motivation | ||||||||||
|
||||||||||
NEAR has been designed with simplicity of onboarding in mind. One of the large hurdles right now is that after creating an implicit or even named account the user does not have NEAR to pay gas fees to interact with apps. | ||||||||||
|
||||||||||
For example, apps that pay user for doing work (like NEARCrowd or Sweatcoin) or free-to-play games. | ||||||||||
|
||||||||||
[Aurora Plus](https://aurora.plus) has shown viability of the relayers that can offer some number of free transactions and a subscription model. Shifting the complexity of dealing with fees to the infrastructure from the user space. | ||||||||||
|
||||||||||
## Rationale and alternatives | ||||||||||
|
||||||||||
Proposed here design provides the easiest for users and developers way to onboard and pay for user transactions. | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
nit: grammar |
||||||||||
There is no requirement on the user account, including user account may not even exist on chain and implicit account can be used. | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
nit: grammar |
||||||||||
|
||||||||||
An alternative is proxy contracts deployed on the user account. | ||||||||||
This design has severe limitations as it requires user to deploy such contract and additional costs for storage. | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
|
||||||||||
## Specification | ||||||||||
|
||||||||||
The main flow of the meta transaction will be as follows: | ||||||||||
- User will sign `DelegateActionMessage` specifying set of actions that they need to be executed. It also includes specific relayer to ensure secure execution. | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
- User sends signed `DelegateAction` data to the relayer | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does a user send There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is the advantage of having two structures: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. +1 for explaining more clearly the role of the two messages. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. also - does user send a DelegateAction or DelegateActionMessage (based on context, I think it is the latter - as this is the one that was created in the previous point) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
- Relayer forms a `Transaction` with `receiver_id` equal to the user's account and `actions: [DelegateAction { ... }]`, and signs it with it's key. Note, that such transaction can contain other actions toward user's account (for example calling a function). | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just to clarify, do you mean here that the protocol specifies that for meta transactions, the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure if that was the goal, but I don't think having this as a singleton is mandatory. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
- This transaction is processed normally, by creating a receipt with copy of the all the actions into the `Receipt`. | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
- When processing `DelegateAction` a number of checks are done (see below), mainly `signature` matching user account's key. | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
- When such `Receipt` with valid `DelegateAction` in actions arrives to the user's account it gets executed. The executed means creation of a new Receipt with `receiver_id: AccountId`, `actions: Action` matching `receiver_id` and `actions` inside `DelegateAction`. | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
- The new `Receipt` looks like normal receipt that would have been originating from user's account, with `predeccessor_id` equal to user's account. | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's unclear what There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please note that using Having said that I think signer_id should be the account that is paying for the attached balance (not gas), and if I understand correctly it is not the relayer, but the user signing the transaction. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
|
||||||||||
### DelegateAction | ||||||||||
|
||||||||||
Delegate action allows for an account to initiate a batch of actions on behalf of the receiving account, allowing to proxy actions. This is used in implementation of meta transactions. | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
|
||||||||||
```rust | ||||||||||
pub struct DelegateAction { | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do we plan to add DelegateAction to the Action enum? |
||||||||||
/// Receiver of the delegated actions. | ||||||||||
receiver_id: AccountId, | ||||||||||
/// List of actions to be executed. | ||||||||||
actions: Vec<Action>, | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is acceptable in this context because an Action can't refer to itself or an ancestor, so recursion is finite. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I meant, rust compiler would not compile such code because of the recursion. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Indeed, a common way to prevent this recursion is to use a
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=14f24298eee755a2a08c7b5b66d8b4fa suggests a fix. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You are right. When I wrote my comment, I didn't know that Borsh supported There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As discussed in the video call, for the purposes of the NEP, the recursive data structure is fine. For |
||||||||||
|
||||||||||
/// Public key that is used to sign this delegated action. | ||||||||||
signer_pk: PublicKey, | ||||||||||
/// Nonce is used to determine that the same delegate action is not sent twice by relayers and should match for given account's `signer_pk`. | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
/// After this action is processed it will increment. | ||||||||||
nonce: Nonce, | ||||||||||
/// Signature of the originating user signing `DelegateActionMessage` formed out of the data in the `DelegateAction`. | ||||||||||
signature: Signature, | ||||||||||
} | ||||||||||
``` | ||||||||||
|
||||||||||
Supporting a batch of `actions` means `DelegateAction` can initiate a complex steps like creating a new account and transferring funds, deploying a contract and executing an initialization function. | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
|
||||||||||
***Validation***: | ||||||||||
To ensure that `DelegateAction` is correct, on receival the verification of signature is done: `verify_signature(hash(message), signer_pk, signature)`. | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
|
||||||||||
The `message` is formed in the next format and must be signed by the user: | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
```rust | ||||||||||
struct DelegateActionMessage { | ||||||||||
/// If not None, should match the `predecessor_id` that have created `DelegateAction`. | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
sender_id: Option<AccountId>, | ||||||||||
/// Matching the given account_id. | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you specify the "given account_id" here please? |
||||||||||
signer_id: AccountId, | ||||||||||
/// Matching the `signer_pk` from `DelegateAction`. | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
public_key: PublicKey, | ||||||||||
/// Nonce for the given `public_key` from `DelegateAction`. | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
nonce: Nonce, | ||||||||||
/// Matching the `receiver_id` from `DelegateAction`. | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
receiver_id: AccountId, | ||||||||||
/// Block hash to ensure validity. | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The field appears to be missing? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes ! as this struct was supposed to be similar to the 'Transaction' struct - it should have a 'block_hash' field here. |
||||||||||
/// Actions matching `actions` from `DelegateAction`. | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
actions: Vec<Action>, | ||||||||||
} | ||||||||||
``` | ||||||||||
|
||||||||||
The next set of security concerns are addressed by this format: | ||||||||||
- If format matches `Transaction`, the relayer can just send it directly, not receiving payment. | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
- Because set of actions can include pay back to the relayer (for example by paying in FT), the `sender_id` is added directly into the message to ensure that nobody else can send this message. | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
- `nonce` is included to ensure that this message can't be replayed again. | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
- `public_key` and `signer_id` are needed to ensure the on the right account, work across rotating keys and fetch correct `nonce`. | ||||||||||
|
||||||||||
The permissions are verified based on kind `AccessKeyPermission` of `signer_pk`: | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
- `AccessKeyPermission::FullAccess`, all actions are allowed. | ||||||||||
- `AccessKeyPermission::FunctionCall`, only a single `FunctionCall` action is allowed in `actions`. | ||||||||||
- `DelegateAction.receiver_id` must match to the `account[public_key].receiver_id` | ||||||||||
- `DelegateAction.actions[0].method_name` must be in the `account[public_key].method_names` | ||||||||||
|
||||||||||
***Outcomes***: | ||||||||||
- If `signature` matches receiver's accounts `signer_pk`, new receipt is created from this account with set of `ActionReceipt { receiver_id, action }` for each item in `actions`. | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
|
||||||||||
#### Errors | ||||||||||
|
||||||||||
**Validation Error** | ||||||||||
- If `signer_pk` does not exist for the given account, the following error will be returned | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
```rust | ||||||||||
/// Signer key for delegate action is missing from given account | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
DelegateActionSignerDoesNotExist | ||||||||||
``` | ||||||||||
|
||||||||||
- If `nonce` does not exist match `signer_pk` for `receiver_id`, the following error will be returned | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
```rust | ||||||||||
/// Nonce must be account[signer_pk].nonce + 1 | ||||||||||
DelegateActionInvalidNonce | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we enforce There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @bowenwang1996 Can you share a reference about how nonces work today? I can't find it in nomicon. |
||||||||||
``` | ||||||||||
|
||||||||||
- If `signature` does not match to the data and `signer_pk` of the given key, the following error will be returned | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
```rust | ||||||||||
/// Signature does not match the provided actions and given signer public key. | ||||||||||
DelegateActionInvalidSignature | ||||||||||
``` | ||||||||||
|
||||||||||
|
||||||||||
See the [DelegateAction specification](specs/RuntimeSepc/Actions.md#DelegateAction) for details. | ||||||||||
|
||||||||||
## Security Implications | ||||||||||
|
||||||||||
Delegate actions do not override `signer_pk`, leaving that to the original signer that initiated transaction (eg relayer in meta transaction case). Although it is possible to override `signer_pk` in the context with one from `DelegateAction`, there is no clear value to do that. | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
|
||||||||||
See ***Validation*** section in [DelegateAction specification](specs/RuntimeSepc/Actions.md#DelegateAction) for security considerations around what user signs and validation of actions with different permissions. | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
|
||||||||||
## Drawbacks | ||||||||||
|
||||||||||
Increases complexity of the the NEAR's transactional model. | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
|
||||||||||
Meta transactions take an extra block to execute, as they first need to be included by the originating account, then routed to the delegate account and only after that to the real destination. | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
|
||||||||||
Delegate actions are not programmable as they require having signatures. Original proposal contained a new `AccessKey` kind that would support programmable delegated actions. On the other hand, that would be limiting without programmability of smart contracts, hence that idea evolved into [Account Extensions](https://github.com/nearprotocol/neps/pull/346). | ||||||||||
|
||||||||||
## Future possibilities | ||||||||||
|
||||||||||
Supporting ZK proofs instead of just signatures can allow for an anonymous transactions, which pay fees to relayers in anonymous way. | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
|
||||||||||
## Copyright | ||||||||||
[copyright]: #copyright | ||||||||||
|
||||||||||
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). |
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.