Skip to content

Commit

Permalink
feat: add more spec to transaction processing and errors (#108)
Browse files Browse the repository at this point in the history
Add more spec to transaction processing and the errors that they generate.
  • Loading branch information
bowenwang1996 authored Sep 2, 2020
1 parent 37fd652 commit 6bc969b
Show file tree
Hide file tree
Showing 4 changed files with 485 additions and 50 deletions.
221 changes: 171 additions & 50 deletions specs/RuntimeSpec/Actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,52 +15,75 @@ pub enum Action {
}
```

Each transaction consists a list of actions to be performed on the `receiver_id` side. Sometimes the `singer_id` equals to `receiver_id`. There is a set of action types when `signer_id` and `receiver_id` are required to be equal. Actions requires arguments and use data from the `Transaction` itself.

// TODO: how to introduce the concept of `sender_id`
Each transaction consists a list of actions to be performed on the `receiver_id` side. Since transactions are first
converted to receipts when they are processed, we will mostly concern ourselves with actions in the context of receipt
processing.

For the following actions, `predecessor_id` and `receiver_id` are required to be equal:
- `DeployContract`
- `Stake`
- `AddKey`
- `DeleteKey`
- `DeleteAccount`

NOTE: if the first action in the action list is `CreateAccount`, `predecessor_id` becomes `receiver_id`
for the rest of the actions until `DeleteAccount`. This gives permission by another account to act on the newly created account.

## CreateAccountAction
_Requirements:_

- _unique `tx.receiver_id`_
- _`public_key` to be `AccessKeyPermission::FullAccess` for the `singer_id`_

`CreateAccountAction` doesn't take any additional arguments, it uses `receiver_id` from Transaction. `receiver_id` is an ID for an account to be created. Account ID should be [valid](Account.md#account-id) and **unique** throughout the system.
```rust
pub struct CreateAccountAction {}
```

**Outcome**:
- creates an account with `id` = `receiver_id`
- sets Account `storage_usage` to `account_cost` (genesis config)
- sets Account `storage_paid_at` to the current block height

NOTE: for the all subsequent actions in the transaction the `signer_id` becomes `receiver_id` until [DeleteAccountAction](#DeleteAccountAction). It allows to execute actions on behalf of the just created account.
### 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
pub struct CreateAccountAction {}
/// A top-level account ID can only be created by registrar.
CreateAccountOnlyByRegistrar {
account_id: AccountId,
registrar_account_id: AccountId,
predecessor_id: AccountId,
}
```

## DeployContractAction

_Requirements:_

- _`tx.signer_id` to be equal to `receiver_id`_
- _`tx.public_key` to be `AccessKeyPermission::FullAccess` for the `singer_id`_
- 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 },
```

**Outcome**:
- sets a code for account
## DeployContractAction

```rust
pub struct DeployContractAction {
pub code: Vec<u8>, // a valid WebAssembly code
pub code: Vec<u8>
}
```

## FunctionCallAction
**Outcome**:
- sets the contract code for account

_Requirements:_
### Errors

- _`tx.public_key` to be `AccessKeyPermission::FullAccess` or `AccessKeyPermission::FunctionCall`_
**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 },
```

Calls a method of a particular contract. See [details](./FunctionCall.md).
**Execution Error**:
- If state or storage is corrupted, it may return `StorageError`.

## FunctionCallAction

```rust
pub struct FunctionCallAction {
Expand All @@ -75,14 +98,9 @@ pub struct FunctionCallAction {
}
```

## TransferAction

_Requirements:_

- _`tx.public_key` to be `AccessKeyPermission::FullAccess` for the `singer_id`_
Calls a method of a particular contract. See [details](./FunctionCall.md).

**Outcome**:
- transfers amount specified in `deposit` from `tx.signer` to a `tx.receiver_id` account
## TransferAction

```rust
pub struct TransferAction {
Expand All @@ -91,12 +109,16 @@ pub struct TransferAction {
}
```

## StakeAction
**Outcome**:
- transfers amount specified in `deposit` from `predecessor_id` to a `receiver_id` account

### Errors

_Requirements:_
**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.

- _`tx.signer_id` to be equal to `receiver_id`_
- _`tx.public_key` to be `AccessKeyPermission::FullAccess` for the `singer_id`_
## StakeAction

```rust
pub struct StakeAction {
Expand All @@ -106,18 +128,51 @@ pub struct StakeAction {
pub public_key: PublicKey,
}
```

**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.
UnsuitableStakingKey { public_key: PublicKey },
```
// TODO: cover staking

**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 },
```
## AddKeyAction

_Requirements:_
- 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 {
account_id: AccountId,
stake: Balance,
locked: Balance,
balance: Balance,
}
```

- _`tx.signer_id` to be equal to `receiver_id`_
- _`tx.public_key` to be `AccessKeyPermission::FullAccess` for the `singer_id`_
- If the staked amount is below the minimum stake threshold, the following error will be returned:
```rust
InsufficientStake {
account_id: AccountId,
stake: Balance,
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.

Associates an [AccessKey](AccessKey) with a `public_key` provided.
## AddKeyAction

```rust
pub struct AddKeyAction {
Expand All @@ -126,30 +181,96 @@ pub struct AddKeyAction {
}
```

## DeleteKeyAction
**Outcome**:
- Adds a new [AccessKey](AccessKey) to the receiver's account and associates it with a `public_key` provided.

### Errors:

**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 },
```

- 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 },
```

_Requirements:_
- 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 }
```

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

- _`tx.signer_id` to be equal to `receiver_id`_
- _`tx.public_key` to be `AccessKeyPermission::FullAccess` for the `singer_id`_
## DeleteKeyAction

```rust
pub struct DeleteKeyAction {
pub public_key: PublicKey,
}
```

## DeleteAccountAction
**Outcome**:
- Deletes the [AccessKey](AccessKey) associated with `public_key`.

### Errors

**Execution Error**:

_Requirements:_
- 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.

- _`tx.signer_id` to be equal to `receiver_id`_
- _`tx.public_key` to be `AccessKeyPermission::FullAccess` for the `singer_id`_
- _`tx.account shouldn't have any locked balance`_
## DeleteAccountAction

```rust
pub struct DeleteAccountAction {
/// The remaining account balance will be transferred to the AccountId below
pub beneficiary_id: AccountId,
}
```

**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.
Loading

0 comments on commit 6bc969b

Please sign in to comment.