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

NEP-366: Meta transactions #366

Merged
merged 9 commits into from
Nov 24, 2022
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Changes to the protocol specification and standards are called NEAR Enhancement
|[0245](https://github.com/near/NEPs/blob/master/neps/nep-0245.md) | Multi Token Standard | @zcstarr @riqi @jriemann @marcos.sun | Review |
|[0297](https://github.com/near/NEPs/blob/master/neps/nep-0297.md) | Events Standard | @telezhnaya | Final |
|[0330](https://github.com/near/NEPs/blob/master/neps/nep-0330.md) | Source Metadata | @BenKurrek | Review |
|[0366](https://github.com/near/NEPs/blob/master/neps/nep-0000.md) | Meta Transactions | @ilblackdragon | Draft |
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
|[0366](https://github.com/near/NEPs/blob/master/neps/nep-0000.md) | Meta Transactions | @ilblackdragon | Draft |
|[0366](https://github.com/near/NEPs/blob/master/neps/nep-0366.md) | Meta Transactions | @ilblackdragon | Draft |




Expand Down
62 changes: 62 additions & 0 deletions neps/nep-0366.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
---
NEP: 366
Title: Meta Transactions
Author: Illia Polosukhin <ilblackdragon@gmail.com>
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 simplify of onboarding in mind. One of the large hurdles right now is that after creating an implicit or even named account user doesn't have NEAR to pay gas fees to interact with apps.
ilblackdragon marked this conversation as resolved.
Show resolved Hide resolved

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.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
Proposed here design provides the easiest for users and developers way to onboard and pay for user transactions.
The proposed design here provides the easiest way for users and developers to onboard and to pay for user transactions.

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.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
There is no requirement on the user account, including user account may not even exist on chain and implicit account can be used.
There are no requirements on the user account. The user account may not even exist on the chain and an implicit account can be used instead.

nit: grammar


An alterantive is a proxy contracts deployed on the user account.
ilblackdragon marked this conversation as resolved.
Show resolved Hide resolved
This design has severe limitations as it requires user to deploy such contract and additional costs for storage.

## Specification

Next changes to protocol are required:
- New Action kind: `DelegateAction(receiver_id, Action, signer_pk, signature)`
- On conversion to Receipt, such action is sent as `DelegateAction` to the `receiver_id`.
- On processing Receipt, it verifies the `signature` over the given account and `signer_pk`. Unwraps into `receiver_id: AccountId`, `action: Action` inside into new Receipt with given `receiver_id` and `action`. This means that `predeccessor_id` of such action has been overriden.

ilblackdragon marked this conversation as resolved.
Show resolved Hide resolved
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.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
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.
Delegate actions do not override `signer_pk`, leaving that to the original signer that initiated transaction (e.g. the relayer in the meta transaction case). Although it is possible to override the `signer_pk` in the context with one from the `DelegateAction`, there is no clear value in that.


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.
Copy link
Contributor

Choose a reason for hiding this comment

The 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.
See the ***Validation*** section in [DelegateAction specification](specs/RuntimeSepc/Actions.md#DelegateAction) for security considerations around what the user signs and the validation of actions with different permissions.


## Drawbacks

Increases complexity of the NEAR's transactional model.
ilblackdragon marked this conversation as resolved.
Show resolved Hide resolved

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.
Copy link
Contributor

Choose a reason for hiding this comment

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


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 progamability of smart contracts, hence that idea evolved into [Account Extensions](https://github.com/nearprotocol/neps/pull/0000).
ilblackdragon marked this conversation as resolved.
Show resolved Hide resolved

## Future possibilities

Supporting ZK proofs instead of just signatures can allow for anonymous transactions, which pay fees to relayers in anonymous way.
ilblackdragon marked this conversation as resolved.
Show resolved Hide resolved

## Copyright
[copyright]: #copyright

Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
82 changes: 82 additions & 0 deletions specs/RuntimeSpec/Actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub enum Action {
AddKey(AddKeyAction),
DeleteKey(DeleteKeyAction),
DeleteAccount(DeleteAccountAction),
DelegateAction(DelegationAction),
}
```

Expand Down Expand Up @@ -276,3 +277,84 @@ DeleteAccountStaking { account_id: AccountId }

**Execution Error**:
- If state or storage is corrupted, a `StorageError` is returned.

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

```rust
pub struct DelegateAction {
/// Receiver of the delegated actions.
receiver_id: AccountId,
/// List of actions to be executed.
actions: Vec<Action>,

/// 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`.
/// 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.

***Validation***:
To ensure that `DelegateAction` is correct, on receival the verification of signature is done: `verify_signature(hash(message), signer_pk, signature)`.

The `message` is formed in the next format and must be signed by the user:
```rust
struct DelegateActionMessage {
/// Matching the `predecessor_id` that have created `DelegateAction`.
sender_id: AccountId,
/// Matching the given account_id.
signer_id: AccountId,
/// Matching the `signer_pk` from `DelegateAction`.
public_key: PublicKey,
/// Nonce for the given `public_key` from `DelegateAction`.
nonce: Nonce,
/// Matching the `receiver_id` from `DelegateAction`.
receiver_id: AccountId,
/// Block hash to ensure validity.
/// Actions matching `actions` from `DelegateAction`.
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.
- 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.
- `nonce` is included to ensure that this message can't be replayed again.
- `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`:
- `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`.

### Errors

**Validation Error**
- If `signer_pk` does not exist for the given account, the following error will be returned
```rust
/// Signer key for delegate action is missing from given account
DelegateActionSignerDoesNotExist
```

- If `nonce` does not exist match `signer_pk` for `receiver_id`, the following error will be returned
```rust
/// Nonce must be account[signer_pk].nonce + 1
DelegateActionInvalidNonce
```

- If `signature` does not match to the data and `signer_pk` of the given key, the following error will be returned
```rust
/// Signature does not match the provided actions and given signer public key.
DelegateActionInvalidSignature
```
2 changes: 1 addition & 1 deletion specs/RuntimeSpec/Receipts.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Receipt

All cross-contract (we assume that each account lives in it's own shard) communication in Near happens trough Receipts.
All cross-contract (we assume that each account lives in it's own shard) communication in Near happens through Receipts.
Receipts are stateful in a sense that they serve not only as messages between accounts but also can be stored in the account storage to await DataReceipts.

Each receipt has a [`predecessor_id`](#predecessor_id) (who sent it) and [`receiver_id`](#receiver_id) the current account.
Expand Down