Skip to content
This repository was archived by the owner on Jan 22, 2025. It is now read-only.

Commit

Permalink
Add rent doc and clarify account doc for app devs
Browse files Browse the repository at this point in the history
  • Loading branch information
ryoqun committed Jun 5, 2020
1 parent 1b1c098 commit e9eea03
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 9 deletions.
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.

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

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.

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

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

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.

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.

Also, there is a way to avoid paying rent with enough account balance called _rent-exempt_.

## Calculation of rent

Note: The rent rate can change in the future. And this applies to the testnet and mainnet-beta.

As of writing, the fixed rent fee is 19.055441478439427 lamports (bytes-epochs). And an epoch is roughly 2 days.

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:

```
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)
```

And the account balance will be reduced to 5,122 lamports at the next epoch even if there is no activity:

```
5,122 = 7,561 (= current balance) - 2,439 (= this account's rent fee for a epoch)
```

This also indicates an account will be immediately removed after creation if the transferred lamports is less than or equal to 2,439.

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

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

0 comments on commit e9eea03

Please sign in to comment.