From 8e0307ee6f5728c03148ba34a1d01bf1b3fc240f Mon Sep 17 00:00:00 2001 From: jakob Date: Thu, 3 Aug 2023 11:38:26 +0200 Subject: [PATCH 1/3] doc: add delegate action to actions specification --- specs/RuntimeSpec/Actions.md | 140 +++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) diff --git a/specs/RuntimeSpec/Actions.md b/specs/RuntimeSpec/Actions.md index 7c5622533..9c63e337c 100644 --- a/specs/RuntimeSpec/Actions.md +++ b/specs/RuntimeSpec/Actions.md @@ -12,6 +12,7 @@ pub enum Action { AddKey(AddKeyAction), DeleteKey(DeleteKeyAction), DeleteAccount(DeleteAccountAction), + Delegate(SignedDelegateAction), } ``` @@ -20,6 +21,7 @@ converted to receipts when they are processed, we will mostly concern ourselves processing. For the following actions, `predecessor_id` and `receiver_id` are required to be equal: + - `DeployContract` - `Stake` - `AddKey` @@ -38,12 +40,14 @@ pub struct CreateAccountAction {} If `receiver_id` has length == 64, this account id is considered to be `hex(public_key)`, meaning creation of account only succeeds if followed up with `AddKey(public_key)` action. **Outcome**: + - creates an account with `id` = `receiver_id` - sets Account `storage_usage` to `account_cost` (genesis config) ### Errors **Execution Error**: + - If the action tries to create a top level account whose length is no greater than 32 characters, and `predecessor_id` is not `registrar_account_id`, which is defined by the protocol, the following error will be returned ```rust @@ -71,11 +75,13 @@ pub struct DeployContractAction { ``` **Outcome**: + - sets the contract code for account ### Errors **Validation Error**: + - if the length of `code` exceeds `max_contract_size`, which is a genesis parameter, the following error will be returned: ```rust /// The size of the contract code exceeded the limit in a DeployContract action. @@ -112,11 +118,13 @@ pub struct TransferAction { ``` **Outcome**: + - transfers amount specified in `deposit` from `predecessor_id` to a `receiver_id` account ### Errors **Execution Error**: + - If the deposit amount plus the existing amount on the receiver account exceeds `u128::MAX`, a `StorageInconsistentState("Account balance integer overflow")` error will be returned. @@ -132,12 +140,14 @@ pub struct StakeAction { ``` **Outcome**: + - A validator proposal that contains the staking public key and the staking amount is generated and will be included in the next block. ### Errors **Validation Error**: + - If the `public_key` is not an ristretto compatible ed25519 key, the following error will be returned: ```rust /// An attempt to stake with a public key that is not convertible to ristretto. @@ -145,6 +155,7 @@ UnsuitableStakingKey { public_key: PublicKey }, ``` **Execution Error**: + - If an account has not staked but it tries to unstake, the following error will be returned: ```rust /// Account is not yet staked, but tries to unstake @@ -152,6 +163,7 @@ TriesToUnstake { account_id: AccountId }, ``` - If an account tries to stake more than the amount of tokens it has, the following error will be returned: + ```rust /// The account doesn't have enough balance to increase the stake. TriesToStake { @@ -163,6 +175,7 @@ TriesToStake { ``` - If the staked amount is below the minimum stake threshold, the following error will be returned: + ```rust InsufficientStake { account_id: AccountId, @@ -170,6 +183,7 @@ InsufficientStake { minimum_stake: Balance, } ``` + The minimum stake is determined by `last_epoch_seat_price / minimum_stake_divisor` where `last_epoch_seat_price` is the seat price determined at the end of last epoch and `minimum_stake_divisor` is a genesis config parameter and its current value is 10. @@ -184,6 +198,7 @@ pub struct AddKeyAction { ``` **Outcome**: + - Adds a new [AccessKey](/DataStructures/AccessKey.md) to the receiver's account and associates it with a `public_key` provided. ### Errors: @@ -191,7 +206,9 @@ pub struct AddKeyAction { **Validation Error**: If the access key is of type `FunctionCallPermission`, the following errors can happen + - If `receiver_id` in `access_key` is not a valid account id, the following error will be returned + ```rust /// Invalid account ID. InvalidAccountId { account_id: AccountId }, @@ -199,6 +216,7 @@ InvalidAccountId { account_id: AccountId }, - If the length of some method name exceed `max_length_method_name`, which is a genesis parameter (current value is 256), the following error will be returned + ```rust /// The length of some method name exceeded the limit in a Add Key action. AddKeyMethodNameLengthExceeded { length: u64, limit: u64 }, @@ -206,6 +224,7 @@ AddKeyMethodNameLengthExceeded { length: u64, limit: u64 }, - If the sum of length of method names (with 1 extra character for every method name) exceeds `max_number_bytes_method_names`, which is a genesis parameter (current value is 2000), the following error will be returned + ```rust /// The total number of bytes of the method names exceeded the limit in a Add Key action. AddKeyMethodNamesNumberOfBytesExceeded { total_number_of_bytes: u64, limit: u64 } @@ -213,10 +232,12 @@ AddKeyMethodNamesNumberOfBytesExceeded { total_number_of_bytes: u64, limit: u64 **Execution Error**: - If an account tries to add an access key with a given public key, but an existing access key with this public key already exists, the following error will be returned + ```rust /// The public key is already used for an existing access key AddKeyAlreadyExists { account_id: AccountId, public_key: PublicKey } ``` + - If state or storage is corrupted, a `StorageError` will be returned. ## DeleteKeyAction @@ -228,6 +249,7 @@ pub struct DeleteKeyAction { ``` **Outcome**: + - Deletes the [AccessKey](/DataStructures/AccessKey.md) associated with `public_key`. ### Errors @@ -235,10 +257,12 @@ pub struct DeleteKeyAction { **Execution Error**: - When an account tries to delete an access key that doesn't exist, the following error is returned + ```rust /// Account tries to remove an access key that doesn't exist DeleteKeyDoesNotExist { account_id: AccountId, public_key: PublicKey } ``` + - `StorageError` is returned if state or storage is corrupted. ## DeleteAccountAction @@ -251,28 +275,144 @@ pub struct DeleteAccountAction { ``` **Outcomes**: + - The account, as well as all the data stored under the account, is deleted and the tokens are transferred to `beneficiary_id`. ### Errors **Validation Error** - If `beneficiary_id` is not a valid account id, the following error will be returned + ```rust /// Invalid account ID. InvalidAccountId { account_id: AccountId }, ``` - If this action is not the last action in the action list of a receipt, the following error will be returned + ```rust /// The delete action must be a final action in transaction DeleteActionMustBeFinal ``` - If the account still has locked balance due to staking, the following error will be returned + ```rust /// Account is staking and can not be deleted DeleteAccountStaking { account_id: AccountId } ``` **Execution Error**: + - If state or storage is corrupted, a `StorageError` is returned. + +## Delegate Actions + +Introduced with [NEP-366](https://github.com/near/NEPs/blob/master/neps/nep-0366.md) to enable meta transactions. + +In summary, a delegate action is an indirect submission of a transaction. +It allows a relayer to do the payment (gas and token costs) for a transaction authored by a user. + +```rust +/// The struct contained in transactions and receipts, inside `Action::Delegate(_)``. +struct SignedDelegateAction { + /// The actual action, see below. + pub delegate_action: DelegateAction, + /// NEP-483 proposal compliant signature + pub signature: Signature, +} +``` + +Note that the signature follows a scheme which is proposed to be standardized in [NEP-483](https://github.com/near/NEPs/pull/483). + + +```rust +/// The struct a user creates and signs to create a meta transaction. +struct DelegateAction { + /// Signer of the delegated actions + pub sender_id: AccountId, + /// Receiver of the delegated actions. + pub receiver_id: AccountId, + /// List of actions to be executed. + /// + /// With the meta transactions MVP defined in NEP-366, nested + /// DelegateActions are not allowed. A separate type is used to enforce it. + pub actions: Vec, + /// Nonce to ensure that the same delegate action is not sent twice by a + /// relayer and should match for given account's `public_key`. + /// After this action is processed it will increment. + pub nonce: Nonce, + /// The maximal height of the block in the blockchain below which the given DelegateAction is valid. + pub max_block_height: BlockHeight, + /// Public key used to sign this delegated action. + pub public_key: PublicKey, +} +``` + +### Outcomes: + +- All actions inside `delegate_action.actions` are submitted with the `delegate_action.sender_id` as the predecessor, `delegate_action.receiver_id` as the receiver, and the relayer (predecessor of `DelegateAction`) as the signer. +- All gas and balance costs for submitting `delegate_action.actions` are subtracted from the relayer. + +### Errors + +**Validation Error**: + +- If the list of Transaction actions contains several `DelegateAction` + +```rust +/// There should be the only one DelegateAction +DelegateActionMustBeOnlyOne +``` + +**Execution Error**: + + +- If the Sender's account doesn't exist + +```rust +/// Happens when TX receiver_id doesn't exist +AccountDoesNotExist +``` + +- If the `signature` does not match the data and the `public_key` of the given key, then the following error will be returned + +```rust +/// Signature does not match the provided actions and given signer public key. +DelegateActionInvalidSignature +``` + +- If the `sender_id` doesn't match the `tx.receiver_id` + +```rust +/// Receiver of the transaction doesn't match Sender of the delegate action +DelegateActionSenderDoesNotMatchTxReceiver +``` + +- If the current block is equal or greater than `max_block_height` + +```rust +/// Delegate action has expired +DelegateActionExpired +``` + +- If the `public_key` does not exist for Sender account + +```rust +/// The given public key doesn't exist for Sender account +DelegateActionAccessKeyError +``` + +- If the `nonce` does match the `public_key` for the `sender_id` + +```rust +/// Nonce must be greater sender[public_key].nonce +DelegateActionInvalidNonce +``` + +- If `nonce` is too large + +```rust +/// DelegateAction nonce is larger than the upper bound given by the block height (block_height * 1e6) +DelegateActionNonceTooLarge +``` From 03d52f99b5a2de2b02a6d2c1d0445aa4aa3fd17f Mon Sep 17 00:00:00 2001 From: jakob Date: Thu, 3 Aug 2023 11:46:15 +0200 Subject: [PATCH 2/3] linter issues --- specs/RuntimeSpec/Actions.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/specs/RuntimeSpec/Actions.md b/specs/RuntimeSpec/Actions.md index 9c63e337c..0df0a96a7 100644 --- a/specs/RuntimeSpec/Actions.md +++ b/specs/RuntimeSpec/Actions.md @@ -50,6 +50,7 @@ If `receiver_id` has length == 64, this account id is considered to be `hex(publ - If the action tries to create a top level account whose length is no greater than 32 characters, and `predecessor_id` is not `registrar_account_id`, which is defined by the protocol, the following error will be returned + ```rust /// A top-level account ID can only be created by registrar. CreateAccountOnlyByRegistrar { @@ -61,6 +62,7 @@ CreateAccountOnlyByRegistrar { - If the action tries to create an account that is neither a top-level account or a subaccount of `predecessor_id`, the following error will be returned + ```rust /// A newly created account must be under a namespace of the creator account CreateAccountNotAllowed { account_id: AccountId, predecessor_id: AccountId }, @@ -83,12 +85,14 @@ pub struct DeployContractAction { **Validation Error**: - if the length of `code` exceeds `max_contract_size`, which is a genesis parameter, the following error will be returned: + ```rust /// The size of the contract code exceeded the limit in a DeployContract action. ContractSizeExceeded { size: u64, limit: u64 }, ``` **Execution Error**: + - If state or storage is corrupted, it may return `StorageError`. ## FunctionCallAction @@ -149,6 +153,7 @@ in the next block. **Validation Error**: - If the `public_key` is not an ristretto compatible ed25519 key, the following error will be returned: + ```rust /// An attempt to stake with a public key that is not convertible to ristretto. UnsuitableStakingKey { public_key: PublicKey }, @@ -231,6 +236,7 @@ AddKeyMethodNamesNumberOfBytesExceeded { total_number_of_bytes: u64, limit: u64 ``` **Execution Error**: + - If an account tries to add an access key with a given public key, but an existing access key with this public key already exists, the following error will be returned ```rust @@ -280,7 +286,8 @@ pub struct DeleteAccountAction { ### Errors -**Validation Error** +**Validation Error**: + - If `beneficiary_id` is not a valid account id, the following error will be returned ```rust @@ -367,7 +374,6 @@ DelegateActionMustBeOnlyOne **Execution Error**: - - If the Sender's account doesn't exist ```rust From 50724b5983407355ea5eb16d3fea141fa469ab55 Mon Sep 17 00:00:00 2001 From: jakob Date: Thu, 3 Aug 2023 11:48:19 +0200 Subject: [PATCH 3/3] more linter issues --- specs/RuntimeSpec/Actions.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/specs/RuntimeSpec/Actions.md b/specs/RuntimeSpec/Actions.md index 0df0a96a7..1c8cd58dd 100644 --- a/specs/RuntimeSpec/Actions.md +++ b/specs/RuntimeSpec/Actions.md @@ -162,6 +162,7 @@ UnsuitableStakingKey { public_key: PublicKey }, **Execution Error**: - If an account has not staked but it tries to unstake, the following error will be returned: + ```rust /// Account is not yet staked, but tries to unstake TriesToUnstake { account_id: AccountId }, @@ -206,7 +207,7 @@ pub struct AddKeyAction { - Adds a new [AccessKey](/DataStructures/AccessKey.md) to the receiver's account and associates it with a `public_key` provided. -### Errors: +### Errors **Validation Error**: @@ -356,7 +357,7 @@ struct DelegateAction { } ``` -### Outcomes: +### Outcomes - All actions inside `delegate_action.actions` are submitted with the `delegate_action.sender_id` as the predecessor, `delegate_action.receiver_id` as the receiver, and the relayer (predecessor of `DelegateAction`) as the signer. - All gas and balance costs for submitting `delegate_action.actions` are subtracted from the relayer.