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

Update docs for eager rent collection #10348

Merged
merged 17 commits into from
Jun 10, 2020
22 changes: 15 additions & 7 deletions docs/src/apps/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Programming Model

An _app_ interacts with a Solana cluster by sending it _transactions_ with one or more _instructions_. The Solana _runtime_ passes those instructions to user-contributed _programs_. An instruction might, for example, tell a program to transfer _lamports_ from one _account_ to another or create an interactive contract that governs how lamports are transfered. Instructions are executed sequentially and atomically. If any instruction is invalid, all account changes are discarded.
An _app_ interacts with a Solana cluster by sending it _transactions_ with one or more _instructions_. The Solana _runtime_ passes those instructions to _programs_ deployed by app developers beforehand. An instruction might, for example, tell a program to transfer _lamports_ from one _account_ to another or create an interactive contract that governs how lamports are transfered. Instructions are executed sequentially and atomically for each transaction. If any instruction is invalid, all account changes in the transaction are discarded.
ryoqun marked this conversation as resolved.
Show resolved Hide resolved

### Accounts and Signatures

Expand All @@ -20,21 +20,29 @@ Each instruction specifies a single program account \(which must be marked execu

![SDK tools](../.gitbook/assets/sdk-tools.svg)

As shown in the diagram above a client creates a program and compiles it to an ELF shared object containing BPF bytecode and sends it to the Solana cluster. The cluster stores the program locally and makes it available to clients via a _program ID_. The program ID is a _public key_ generated by the client and is used to reference the program in subsequent transactions.
As shown in the diagram above, a program author creates a program and compiles it to an ELF shared object containing BPF bytecode and uploads it to the Solana cluster with a special _deploy_ transaction. The cluster makes it available to clients via a _program ID_. The program ID is a _public key_ specified when deploying and is used to reference the program in subsequent transactions.
ryoqun marked this conversation as resolved.
Show resolved Hide resolved

A program may be written in any programming language that can target the Berkley Packet Filter \(BPF\) safe execution environment. The Solana SDK offers the best support for C/C++ and Rust programs, which are compiled to BPF using the [LLVM compiler infrastructure](https://llvm.org).

## Storing State between Transactions

If the program needs to store state between transactions, it does so using _accounts_. Accounts are similar to files in operating systems such as Linux. Like a file, an account may hold arbitrary data and that data persists beyond the lifetime of a program. Also like a file, an account includes metadata that tells the runtime who is allowed to access the data and how. Unlike a file, the account includes metadata for the lifetime of the file. That lifetime is expressed in "tokens", which is a number of fractional native tokens, called _lamports_. Accounts are held in validator memory and pay "rent" to stay there. Each validator periodically scan all accounts and collects rent. Any account that drops to zero lamports is purged.
If the program needs to store state between transactions, it does so using _accounts_. Accounts are similar to files in operating systems such as Linux. Like a file, an account may hold arbitrary data and that data persists beyond the lifetime of a program. Also like a file, an account includes metadata that tells the runtime who is allowed to access the data and how.
ryoqun marked this conversation as resolved.
Show resolved Hide resolved

If an account is marked "executable", it will only be used by a _loader_ to run programs. For example, a BPF-compiled program is marked executable and loaded by the BPF loader. No program is allowed to modify the contents of an executable account.
Unlike a file, the account includes metadata for the lifetime of the file. That lifetime is expressed in "tokens", which is a number of fractional native tokens, called _lamports_. Accounts are held in validator memory and pay ["rent"](apps/rent.d) to stay there. Each validator periodically scan all accounts and collects rent. Any account that drops to zero lamports is purged.

An account also includes "owner" metadata. The owner is a program ID. The runtime grants the program write access to the account if its ID matches the owner. If an account is not owned by a program, the program is permitted to read its data and credit the account.
In the same way that a Linux user uses a path to look up a file, a Solana client uses _addresses_ to look up accounts. The address is usually plain a 256-bit public key and presented as Base58 on user interfaces. To create an account with a public key, the client generates a _keypair_ and registers its public key using the `CreateAccount` instruction with preallocated fixed storage size in bytes. In fact, the account address can be arbitrary 32-bytes binary. As such there is a mechanism for derived addresses for advanced use (`CreateAccountWithSeed`).
ryoqun marked this conversation as resolved.
Show resolved Hide resolved

In the same way that a Linux user uses a path to look up a file, a Solana client uses public keys to look up accounts. To create an account, the client generates a _keypair_ and registers its public key using the `CreateAccount` instruction. The account created by `CreateAccount` is called a _system account_ and is owned by a built-in program called the System program. The System program allows clients to transfer lamports and assign account ownership.
## Ownership of Accounts and Assignment to Programs

The runtime only permits the owner to debit the account or modify its data. The program then defines additional rules for whether the client can modify accounts it owns. In the case of the System program, it allows users to transfer lamports by recognizing transaction signatures. If it sees the client signed the transaction using the keypair's _private key_, it knows the client authorized the token transfer.
The created account is initialized to be _owned_ by a built-in program called the System program and is called a _system account_ aptly. An account includes "owner" metadata. The owner is a program ID. The runtime grants the program write access to the account if its ID matches the owner. For the case of the System program, the runtime allows clients to transfer lamports and importantly _assign_ account ownership, meaning changing owner to different program ID. If an account is not owned by a program, the program is only permitted to read its data and credit the account.

Also, if an account is marked "executable" in metadata, it will only be used by a _loader_ to run programs. Moreover, programs are just executable accounts owned by a _loader_. For example, a BPF-compiled program is marked executable and loaded by the BPF loader when executing its transactions. This is like a file in a sense that the `ls` command is just a executable file and loaded by the `ld` loader. However, no program is allowed to modify the contents of an executable account once deployed unlike a file as a blockchain security assumption.

## Runtime Capability of Programs on Accounts

The runtime only permits the owner program to debit the account or modify its data. The program then defines additional rules for whether the client can modify accounts it owns. In the case of the System program, it allows users to transfer lamports by recognizing transaction signatures. If it sees the client signed the transaction using the keypair's _private key_, it knows the client authorized the token transfer.

In other words, the entire set of assigned accounts can be regarded as a key-value store where key is the account address and value is program-specific arbitrary binary data. A program author can decide how to manage the program's whole state as possibly many accounts.

After the runtime executes each of the transaction's instructions, it uses the account metadata to verify that none of the access rules were violated. If a program violates an access rule, the runtime discards all account changes made by all instructions and marks the transaction as failed.

Expand Down
46 changes: 45 additions & 1 deletion docs/src/apps/rent.md
Original file line number Diff line number Diff line change
@@ -1 +1,45 @@
# Accounts and Rent
# Storage Rent for Accounts
ryoqun marked this conversation as resolved.
Show resolved Hide resolved

Keeping accounts alive incurs storage cost called _rent_ because the cluster must actively maintain the data to process any future transactions on it. This is not like Bitcoin or Ethereum, where account storages don't incur any costs.
ryoqun marked this conversation as resolved.
Show resolved Hide resolved

The rent is debited from account's balance by the runtime upon the first access in the current epoch by transactions or once per an epoch if there is no transaction. The rent is up-front for the next epoch. If an account can't pay rent for the next epoch, it's purged immediately. The fee is currently a fixed rate, measured in bytes-times-epochs. The fee may change in the future.
ryoqun marked this conversation as resolved.
Show resolved Hide resolved

Also, there is a way to avoid paying rent with enough account balance called _rent-exempt_.
ryoqun marked this conversation as resolved.
Show resolved Hide resolved

## Calculation of rent

Note: The rent rate can change in the future. And this applies to the testnet and mainnet-beta.
ryoqun marked this conversation as resolved.
Show resolved Hide resolved

As of writing, the fixed rent fee is 19.055441478439427 lamports (bytes-epochs). And an epoch is roughly 2 days.
ryoqun marked this conversation as resolved.
Show resolved Hide resolved

Firstly, the rent calculation considers the size of account including the metadata including its address, owner, lamports, etc. Thus the rent fee starts from 128 bytes as the minimum to be rented even if an account has no data.

For example, if an no-data account is created with the initial transfer of 10,000 lamports. The rent is immediately debited from it on creation, resulting in the balance of 7,561 lamports.

You can calculate like this:
ryoqun marked this conversation as resolved.
Show resolved Hide resolved

```
7,561 = 10,000 (= transfered lamports) - 2,439 (= this account's rent fee for a epoch)
2,439 = 19.055441478439427 (= rent rate) * 128 bytes (= minimum account size) * 1 (= epoch)
ryoqun marked this conversation as resolved.
Show resolved Hide resolved
```

And the account balance will be reduced to 5,122 lamports at the next epoch even if there is no activity:
ryoqun marked this conversation as resolved.
Show resolved Hide resolved

```
5,122 = 7,561 (= current balance) - 2,439 (= this account's rent fee for a epoch)
ryoqun marked this conversation as resolved.
Show resolved Hide resolved
```

This also indicates an account will be immediately removed after creation if the transferred lamports is less than or equal to 2,439.
ryoqun marked this conversation as resolved.
Show resolved Hide resolved

## Rent exemption

Alternatively, an account can be exempt from rent collection entirely by depositing certain amount of lamports. Such minimum amount is defined as the 2 years worth of rent fee.
ryoqun marked this conversation as resolved.
Show resolved Hide resolved
Program executable account must be rent-exempt to avoid to be purged.

Note: there is an RPC endpoint specifically to calculate this (getMinimumBalanceForRentExemption). Apps should rely on it. The following calculation is for illustrative only.

For example, 105,290,880 lamports (=~ 0.105 SOL) is needed to be rent-exempt for a program executable with the size of 15,000 bytes:
ryoqun marked this conversation as resolved.
Show resolved Hide resolved

```
105,290,880 = 19.055441478439427 (= fee rate) * (128 + 15_000)(= account size) * ((365.25/2) * 2)(=epochs in 2 years)
```
2 changes: 1 addition & 1 deletion docs/src/implemented-proposals/rent.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Finally, rent collection happens according to the protocol-level account updates

### Current design rationale

Under the preceding design, it is NOT possible to have accounts that linger, never get touched, and never have to pay rent. Accounts are always pay rent exactly once for each epoch.
Under the preceding design, it is NOT possible to have accounts that linger, never get touched, and never have to pay rent. Accounts are always pay rent exactly once for each epoch, except rent-exempt, sysvar and executable accounts.

This is intended design choice. Otherwise, it would be possible to trigger unauthorized rent collection with `Noop` instruction by anyone who may unfairly profit the rent (a leader at the moment) or save the rent given anticipated fluctuating rent cost.

Expand Down