Flesh out development docs (#13318)

* flesh out development docs

* nits
This commit is contained in:
Jack May
2020-11-03 12:53:17 -08:00
committed by GitHub
parent 546915ee12
commit 3d5e778d5d
35 changed files with 1757 additions and 540 deletions

View File

@@ -0,0 +1,141 @@
---
title: Backward Compatibility Policy
---
As the Solana developer ecosystem grows, so does the need for clear expectations around
breaking API and behavior changes affecting applications and tooling built for Solana.
In a perfect world, Solana development could continue at a very fast pace without ever
causing issues for existing developers. However, some compromises will need to be made
and so this document attempts to clarify and codify the process for new releases.
### Expectations
- Solana software releases include APIs, SDKs, and CLI tooling (with a few [exceptions](#exceptions)).
- Solana software releases follow semantic versioning, more details below.
- Software for a `MINOR` version release will be compatible across all software on the
same `MAJOR` version.
### Deprecation Process
1. In any `PATCH` or `MINOR` release, a feature, API, endpoint, etc. could be marked as deprecated.
2. According to code upgrade difficulty, some features will be remain deprecated for a few release
cycles.
3. In a future `MAJOR` release, deprecated features will be removed in an incompatible way.
### Release Cadence
The Solana RPC API, Rust SDK, CLI tooling, and BPF Program SDK are all updated and shipped
along with each Solana software release and should always be compatible between `PATCH`
updates of a particular `MINOR` version release.
#### Release Channels
- `edge` software that contains cutting-edge features with no backward compatibility policy
- `beta` software that runs on the Solana Tour de SOL testnet cluster
- `stable` software that run on the Solana Mainnet Beta and Devnet clusters
#### Major Releases (x.0.0)
`MAJOR` version releases (e.g. 2.0.0) may contain breaking changes and removal of previously
deprecated features. Client SDKs and tooling will begin using new features and endpoints
that were enabled in the previous `MAJOR` version.
#### Minor Releases (1.x.0)
New features and proposal implementations are added to _new_ `MINOR` version
releases (e.g. 1.4.0) and are first run on Solana's Tour de SOL testnet cluster. While running
on the testnet, `MINOR` versions are considered to be in the `beta` release channel. After
those changes have been patched as needed and proven to be reliable, the `MINOR` version will
be upgraded to the `stable` release channel and deployed to the Mainnet Beta cluster.
#### Patch Releases (1.0.x)
Low risk features, non-breaking changes, and security and bug fixes are shipped as part
of `PATCH` version releases (e.g. 1.0.11). Patches may be applied to both `beta` and `stable`
release channels.
### RPC API
Patch releases:
- Bug fixes
- Security fixes
- Endpoint / feature deprecation
Minor releases:
- New RPC endpoints and features
Major releases:
- Removal of deprecated features
### Rust Crates
* [`solana-sdk`](https://docs.rs/solana-sdk/) - Rust SDK for creating transactions and parsing account state
* [`solana-program`](https://docs.rs/solana-program/) - Rust SDK for writing programs
* [`solana-client`](https://docs.rs/solana-client/) - Rust client for connecting to RPC API
* [`solana-cli-config`](https://docs.rs/solana-cli-config/) - Rust client for managing Solana CLI config files
Patch releases:
- Bug fixes
- Security fixes
- Performance improvements
Minor releases:
- New APIs
Major releases
- Removal of deprecated APIs
- Backwards incompatible behavior changes
### CLI Tools
Patch releases:
- Bug and security fixes
- Performance improvements
- Subcommand / argument deprecation
Minor releases:
- New subcommands
Major releases:
- Switch to new RPC API endpoints / configuration introduced in the previous major version.
- Removal of deprecated features
### Runtime Features
New Solana runtime features are feature-switched and manually activated. Runtime features
include: the introduction of new native programs, sysvars, and syscalls; and changes to
their behavior. Feature activation is cluster agnostic, allowing confidence to be built on
Testnet before activation on Mainnet-beta.
The release process is as follows:
1. New runtime feature is included in a new release, deactivated by default
2. Once sufficient staked validators upgrade to the new release, the runtime feature switch
is activated manually with an instruction
3. The feature takes effect at the beginning of the next epoch
### Infrastructure Changes
#### Public API Nodes
Solana provides publicly available RPC API nodes for all developers to use. The Solana team
will make their best effort to communicate any changes to the host, port, rate-limiting behavior,
availability, etc. However, we recommend that developers rely on their own validator nodes to
discourage dependence upon Solana operated nodes.
#### Local cluster scripts and Docker images
Breaking changes will be limited to `MAJOR` version updates. `MINOR` and `PATCH` updates should always
be backwards compatible.
### Exceptions
#### Web3 JavaScript SDK
The Web3.JS SDK also follows semantic versioning specifications but is shipped separately from Solana
software releases.
#### Attack Vectors
If a new attack vector is discovered in existing code, the above processes may be
circumvented in order to rapidly deploy a fix, depending on the severity of the issue.

View File

@@ -0,0 +1,63 @@
---
title: "Builtin programs"
---
Solana contains a small handful of builtin programs, which are required to run
validator nodes. Unlike third-party programs, the builtin programs are part of
the validator implementation and can be upgraded as part of cluster upgrades.
Upgrades may occur to add features, fix bugs, or improve performance. Interface
changes to individual instructions should rarely, if ever, occur. Instead, when
change is needed, new instructions are added and previous ones are marked
deprecated. Apps can upgrade on their own timeline without concern of breakages
across upgrades.
The builtin programs include the System, Config, Stake, Vote, and BPFLoader
programs. For each, we provide the program ID and describe each supported
instruction. A transaction can mix and match instructions from different
programs, as well include instructions from third-party programs.
## System Program
Create accounts and transfer lamports between them
- Program ID: `11111111111111111111111111111111`
- Instructions: [SystemInstruction](https://docs.rs/solana-sdk/VERSION_FOR_DOCS_RS/solana_sdk/system_instruction/enum.SystemInstruction.html)
## Config Program
Add configuration data to the chain and the list of public keys that are permitted to modify it
- Program ID: `Config1111111111111111111111111111111111111`
- Instructions: [config_instruction](https://docs.rs/solana-config-program/VERSION_FOR_DOCS_RS/solana_config_program/config_instruction/index.html)
Unlike the other programs, the Config program does not define any individual
instructions. It has just one implicit instruction, a "store" instruction. Its
instruction data is a set of keys that gate access to the account, and the
data to store in it.
## Stake Program
Create stake accounts and delegate it to validators
- Program ID: `Stake11111111111111111111111111111111111111`
- Instructions: [StakeInstruction](https://docs.rs/solana-stake-program/VERSION_FOR_DOCS_RS/solana_stake_program/stake_instruction/enum.StakeInstruction.html)
## Vote Program
Create vote accounts and vote on blocks
- Program ID: `Vote111111111111111111111111111111111111111`
- Instructions: [VoteInstruction](https://docs.rs/solana-vote-program/VERSION_FOR_DOCS_RS/solana_vote_program/vote_instruction/enum.VoteInstruction.html)
## BPF Loader
Add programs to the chain and execute them.
- Program ID: `BPFLoader1111111111111111111111111111111111`
- Instructions: [LoaderInstruction](https://docs.rs/solana-sdk/VERSION_FOR_DOCS_RS/solana_sdk/loader_instruction/enum.LoaderInstruction.html)
The BPF Loader marks itself as its "owner" of the executable account it
creates to store your program. When a user invokes an instruction via a
program ID, the Solana runtime will load both your executable account and its
owner, the BPF Loader. The runtime then passes your program to the BPF Loader
to process the instruction.

View File

@@ -0,0 +1,5 @@
---
title: Web3 JavaScript API
---
See [solana-web3](https://solana-labs.github.io/solana-web3.js/).

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,118 @@
---
title: "Debugging"
---
Solana programs run on-chain, so debugging them in the wild can be challenging.
To make debugging programs easier, developers can write unit tests that directly
test their program's execution via the Solana runtime, or run a local cluster
that will allow RPC clients to interact with their program.
## Running unit tests
TODO
## Running on a Local Cluster
TODO
## Transaction Simulation
TODO
## Logging
During program execution both the runtime and the program log status and error
messages.
For information about how to log from a program see the language specific
documentation:
- [Logging from a Rust program](developing-rust.md#logging)
- [Logging from a C program](developing-c.md#logging)
When running a local cluster the logs are written to stdout as long as they are
enabled via the `RUST_LOG` log mask. From the perspective of program
development it is helpful to focus on just the runtime and program logs and not
the rest of the cluster logs. To focus in on program specific information the
following log mask is recommended:
`export
RUST_LOG=solana_runtime::system_instruction_processor=trace,solana_runtime::message_processor=info,solana_bpf_loader=debug,solana_rbpf=debug`
Log messages coming directly from the program (not the runtime) will be
displayed in the form:
`Program log: <user defined message>`
## Error Handling
The amount of information that can be communicated via a transaction error is
limited but there are many points of possible failures. The following are
possible failure points and information about what errors to expect and where to
get more information:
- The BPF loader may fail to parse the program, this should not happen since the
loader has already _finalized_ the program's account data.
- `InstructionError::InvalidAccountData` will be returned as part of the
transaction error.
- The BPF loader may fail to setup the program's execution environment
- `InstrucitonError::Custom(0x0b9f_0001)` will be returned as part of the
transaction error. "0x0b9f_0001" is the hexadecimal representation of
[`VirtualMachineCreationFailed`](https://github.com/solana-labs/solana/blob/bc7133d7526a041d1aaee807b80922baa89b6f90/programs/bpf_loader/src/lib.rs#L44).
- The BPF loader may have detected a fatal error during program executions
(things like panics, memory violations, system call errors, etc...)
- `InstrucitonError::Custom(0x0b9f_0002)` will be returned as part of the
transaction error. "0x0b9f_0002" is the hexadecimal representation of
[`VirtualMachineFailedToRunProgram`](https://github.com/solana-labs/solana/blob/bc7133d7526a041d1aaee807b80922baa89b6f90/programs/bpf_loader/src/lib.rs#L46).
- The program itself may return an error
- `InstrucitonError::Custom(<user defined value>)` will be returned. The
"user defined value" must not conflict with any of the [builtin runtime
program
errors](https://github.com/solana-labs/solana/blob/bc7133d7526a041d1aaee807b80922baa89b6f90/sdk/program/src/program_error.rs#L87).
Programs typically use enumeration types to define error codes starting at
zero so they won't conflict.
In the case of `VirtualMachineFailedToRunProgram` errors, more information about
the specifics of what failed are written to the [program's execution
logs](debugging.md#logging).
For example, an access violation involving the stack will look something like
this:
`BPF program 4uQeVj5tqViQh7yWWGStvkEG1Zmhx6uasJtWCJziofM failed: out of bounds
memory store (insn #615), addr 0x200001e38/8 `
## Monitoring Compute Budget Consumption
The program can log the remaining number of compute units it will be allowed
before program execution is halted. Programs can use these logs to wrap
operations they wish to profile.
- [Log the remaining compute units from a Rust
program](developing-rust.md#compute-budget)
- [Log the remaining compute units from a C
program](developing-c.md#compute-budget)
See [compute
budget](developing/programming-model/../../../programming-model/compute-budget.md)
for more information.
## ELF Dump
The BPF shared object internals can be dumped to a text file to gain more
insight into a program's composition and what it may be doing at runtime.
- [Create a dump file of a Rust program](developing-rust.md#elf-dump)
- [Create a dump file of a C program](developing-c.md#elf-dump)
## Instruction Tracing
During execution the runtime BPF interpreter can be configured to log a trace
message for each BPF instruction executed. This can be very helpful for things
like pin-pointing the runtime context leading up to a memory access violation.
The trace logs together with the [ELF dump](#elf-dump) can provide a lot of
insight (though the traces produce a lot of information).
To turn on BPF interpreter trace messages in a local cluster configure the
`solana_rbpf` level in `RUST_LOG` to `trace`. For example:
`export RUST_LOG=solana_rbpf=trace`

View File

@@ -0,0 +1,20 @@
---
title: "Deploying"
---
![SDK tools](/img/sdk-tools.svg)
As shown in the diagram above, a program author creates a program, 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 an _address_ specified when
deploying and is used to reference the program in subsequent transactions.
Upon a successful deployment the account that holds the program is marked
executable and its account data become permanently immutable. If any changes
are required to the program (features, patches, etc...) the new program must be
deployed to a new program ID.
The Solana command line interface supports deploying programs, for more
information see the [`deploy`](cli/usage.md#deploy-program) command line usage
documentation.

View File

@@ -0,0 +1,213 @@
---
title: "Developing with C"
---
Solana supports writing on-chain programs using the C and C++ programming
languages.
## Project Layout
C projects are laid out as follows:
```
/src/<program name>
/makefile
```
The `makefile` should contain the following:
```bash
OUT_DIR := <path to place to resulting shared object>
include ~/.local/share/solana/install/active_release/bin/sdk/bpf/c/bpf.mk
```
The bpf-sdk may not be in the exact place specified above but if you setup your
environment per [How to Build](#how-to-build) then it should be.
Take a look at
[helloworld](https://github.com/solana-labs/example-helloworld/tree/master/src/program-c)
for an example of a C program.
## How to Build
First setup the environment:
- Install the latest Rust stable from https://rustup.rs
- Install the latest Solana command-line tools from
https://docs.solana.com/cli/install-solana-cli-tools
Then build using make:
```bash
make -C <program directory>
```
## How to Test
Solana uses the [Criterion](https://github.com/Snaipe/Criterion) test framework
and tests are executed each time the program is built [How to
Build](#how-to-build)].
To add tests, create a new file next to your source file named `test_<program
name>.c` and populate it with criterion test cases. For an example see the
[helloworld C
tests](https://github.com/solana-labs/example-helloworld/blob/master/src/program-c/src/helloworld/test_helloworld.c)
or the [Criterion docs](https://criterion.readthedocs.io/en/master) for
information on how to write a test case.
## Program Entrypoint
Programs export a known entrypoint symbol which the Solana runtime looks up and
calls when invoking a program. Solana supports multiple [versions of the BPF
loader](overview.md#versions) and the entrypoints may vary between them.
Programs must be written for and deployed to the same loader. For more details
see the [overview](overview#loaders).
Currently there are two supported loaders [BPF
Loader](https://github.com/solana-labs/solana/blob/7ddf10e602d2ed87a9e3737aa8c32f1db9f909d8/sdk/program/src/bpf_loader.rs#L17)
and [BPF loader
deprecated](https://github.com/solana-labs/solana/blob/7ddf10e602d2ed87a9e3737aa8c32f1db9f909d8/sdk/program/src/bpf_loader_deprecated.rs#L14)
They both have the same raw entrypoint definition, the following is the raw
symbol that the runtime looks up and calls:
```c
extern uint64_t entrypoint(const uint8_t *input)
```
This entrypoint takes a generic byte array which contains the serialized program
parameters (program id, accounts, instruction data, etc...). To deserialize the
parameters each loader contains its own [helper function](#Serialization).
Refer to [helloworld's use of the
entrypoint](https://github.com/solana-labs/example-helloworld/blob/bc0b25c0ccebeff44df9760ddb97011558b7d234/src/program-c/src/helloworld/helloworld.c#L37)
as an example of how things fit together.
### Serialization
Refer to [helloworld's use of the deserialization
function](https://github.com/solana-labs/example-helloworld/blob/bc0b25c0ccebeff44df9760ddb97011558b7d234/src/program-c/src/helloworld/helloworld.c#L43).
Each loader provides a helper function that deserializes the program's input
parameters into C types:
- [BPF Loader
deserialization](https://github.com/solana-labs/solana/blob/d2ee9db2143859fa5dc26b15ee6da9c25cc0429c/sdk/bpf/c/inc/solana_sdk.h#L304)
- [BPF Loader deprecated
deserialization](https://github.com/solana-labs/solana/blob/8415c22b593f164020adc7afe782e8041d756ddf/sdk/bpf/c/inc/deserialize_deprecated.h#L25)
Some programs may want to perform deserialzaiton themselves and they can by
providing their own implementation of the [raw entrypoint](#program-entrypoint).
Take note that the provided deserialization functions retain references back to
the serialized byte array for variables that the program is allowed to modify
(lamports, account data). The reason for this is that upon return the loader
will read those modifications so they may be committed. If a program implements
their own deserialization function they need to ensure that any modifications
the program wishes to commit must be written back into the input byte array.
Details on how the loader serializes the program inputs can be found in the
[Input Parameter Serialization](overview.md#input-parameter-serialization) docs.
## Data Types
The loader's deserialization helper function populates the
[SolParameters](https://github.com/solana-labs/solana/blob/8415c22b593f164020adc7afe782e8041d756ddf/sdk/bpf/c/inc/solana_sdk.h#L276)
structure:
```c
/**
* Structure that the program's entrypoint input data is deserialized into.
*/
typedef struct {
SolAccountInfo* ka; /** Pointer to an array of SolAccountInfo, must already
point to an array of SolAccountInfos */
uint64_t ka_num; /** Number of SolAccountInfo entries in `ka` */
const uint8_t *data; /** pointer to the instruction data */
uint64_t data_len; /** Length in bytes of the instruction data */
const SolPubkey *program_id; /** program_id of the currently executing program */
} SolParameters;
```
'ka' is an ordered array of the accounts referenced by the instruction and
represented as a
[SolAccountInfo](https://github.com/solana-labs/solana/blob/8415c22b593f164020adc7afe782e8041d756ddf/sdk/bpf/c/inc/solana_sdk.h#L173)
structures. An account's place in the array signifies its meaning, for example,
when transferring lamports an instruction may define the first account as the
source and the second as the destination.
The members of the `SolAccountInfo` structure are read-only except for
`lamports` and `data`. Both may be modified by the program in accordance with
the [runtime enforcement
policy](developing/programming-model/accounts.md#policy). When an instruction
reference the same account multiple times there may be duplicate
`SolAccountInfo` entries in the array but they both point back to the original
input byte array. A program should handle these case delicately to avoid
overlapping read/writes to the same buffer. If a program implements their own
deserialization function care should be taken to handle duplicate accounts
appropriately.
`data` is the general purpose byte array from the [instruction's instruction
data](developing/programming-model/transactions.md#instruction-data) being
processed.
`program_id` is the public key of the currently executing program.
## Heap
C programs can allocate memory via the system call
[`calloc`](https://github.com/solana-labs/solana/blob/c3d2d2134c93001566e1e56f691582f379b5ae55/sdk/bpf/c/inc/solana_sdk.h#L245)
or implement their own heap on top of the 32KB heap region starting at virtual
address x300000000. The heap region is also used by `calloc` so if a program
implements their own heap it should not also call `calloc`.
## Logging
The runtime provides two system calls that take data and log it to the program
logs.
- [`sol_log(const
char*)`](https://github.com/solana-labs/solana/blob/d2ee9db2143859fa5dc26b15ee6da9c25cc0429c/sdk/bpf/c/inc/solana_sdk.h#L128)
- [`sol_log_64(uint64_t, uint64_t, uint64_t, uint64_t,
uint64_t)`](https://github.com/solana-labs/solana/blob/d2ee9db2143859fa5dc26b15ee6da9c25cc0429c/sdk/bpf/c/inc/solana_sdk.h#L134)
The [debugging](debugging.md#logging) section has more information about working
with program logs.
## Compute Budget
Use the system call
[`sol_log_compute_units()`](https://github.com/solana-labs/solana/blob/d3a3a7548c857f26ec2cb10e270da72d373020ec/sdk/bpf/c/inc/solana_sdk.h#L140)
to log a message containing the remaining number of compute units the program
may consume before execution is halted
See [compute
budget](developing/programming-model/../../../programming-model/compute-budget.md)
for more information.
## ELF Dump
The BPF shared object internals can be dumped to a text file to gain more
insight into a program's composition and what it may be doing at runtime. The
dump will contain both the ELF information as well as a list of all the symbols
and the instructions that implement them. Some of the BPF loader's error log
messages will reference specific instruction numbers where the error occurred.
These references can be looked up in the ELF dump to identify the offending
instruction and its context.
To create a dump file:
```bash
$ cd <program directory>
$ make dump_<program name>
```
## Examples
TODO
### Logging
### Transferring Lamports
### Writing Account Data
### Custom Heap
### Cross-program Invocations

View File

@@ -0,0 +1,315 @@
---
title: "Developing with Rust"
---
Solana supports writing on-chain programs using the
[Rust](https://www.rust-lang.org/) programming language.
## Project Layout
Solana Rust programs follow the typical [Rust project
layout](https://doc.rust-lang.org/cargo/guide/project-layout.html):
```
/inc/
/src/
/Cargo.toml
```
But must also include:
```
/Xargo.toml
```
Which must contain:
```
[target.bpfel-unknown-unknown.dependencies.std]
features = []
```
Solana Rust programs may depend directly on each other in order to gain access
to instruction helpers when making [cross-program
invocations](developing/../../programming-model/cpi.md). When doing so it's
important to not pull in the dependent program's entrypoint symbols because they
may conflict with the program's own. To avoid this ,programs should define an
`exclude_entrypoint` feature in `Cargo.toml`j and use to exclude the entrypoint.
- [Define the
feature](https://github.com/solana-labs/solana-program-library/blob/a5babd6cbea0d3f29d8c57d2ecbbd2a2bd59c8a9/token/program/Cargo.toml#L12)
- [Exclude the
entrypoint](https://github.com/solana-labs/solana-program-library/blob/a5babd6cbea0d3f29d8c57d2ecbbd2a2bd59c8a9/token/program/src/lib.rs#L12)
Then when other programs include this program as a dependency, they should do so
using the `exclude_entrypoint` feature.
- [Include without
entrypoint](https://github.com/solana-labs/solana-program-library/blob/a5babd6cbea0d3f29d8c57d2ecbbd2a2bd59c8a9/token-swap/program/Cargo.toml#L19)
## Project Dependencies
At a minimum, Solana Rust programs must pull in the
[solana-program](https://crates.io/crates/solana-program) crate.
Programs are constrained to run deterministically, so random numbers are not
available. Sometimes a program may depend on a crate that depends itself on
`rand` even if the program does not use any of the random number functionality.
If a program depends on `rand`, the compilation will fail because there is no
`get-random` support for Solana. The error will typically look like this:
```
error: target is not supported, for more information see: https://docs.rs/getrandom/#unsupported-targets
--> /Users/jack/.cargo/registry/src/github.com-1ecc6299db9ec823/getrandom-0.1.14/src/lib.rs:257:9
|
257 | / compile_error!("\
258 | | target is not supported, for more information see: \
259 | | https://docs.rs/getrandom/#unsupported-targets\
260 | | ");
| |___________^
```
To work around this dependency issue, add the following dependency to the
program's `Cargo.toml`:
```
getrandom = { version = "0.1.14", features = ["dummy"] }
```
## How to Build
First setup the environment:
- Install the latest Rust stable from https://rustup.rs/
- Install the latest Solana command-line tools from
https://docs.solana.com/cli/install-solana-cli-tools
The normal cargo build is available for building programs against your host
machine which can be used for unit testing:
```bash
$ cargo build
```
To build a specific program, such as SPL Token, for the Solana BPF target which
can be deployed to the cluster:
```bash
$ cd <the program directory>
$ cargo build-bpf
```
## How to Test
TODO
## Program Entrypoint
Programs export a known entrypoint symbol which the Solana runtime looks up and
calls when invoking a program. Solana supports multiple [versions of the BPF
loader](overview.md#versions) and the entrypoints may vary between them.
Programs must be written for and deployed to the same loader. For more details
see the [overview](overview#loaders).
Currently there are two supported loaders [BPF
Loader](https://github.com/solana-labs/solana/blob/7ddf10e602d2ed87a9e3737aa8c32f1db9f909d8/sdk/program/src/bpf_loader.rs#L17)
and [BPF loader
deprecated](https://github.com/solana-labs/solana/blob/7ddf10e602d2ed87a9e3737aa8c32f1db9f909d8/sdk/program/src/bpf_loader_deprecated.rs#L14)
They both have the same raw entrypoint definition, the following is the raw
symbol that the runtime looks up and calls:
```rust
#[no_mangle]
pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u64;
```
This entrypoint takes a generic byte array which contains the serialized program
parameters (program id, accounts, instruction data, etc...). To deserialize the
parameters each loader contains its own wrapper macro that exports the raw
entrypoint, deserializes the parameters, calls a user defined instruction
processing function, and returns the results.
You can find the entrypoint macros here:
- [BPF Loader's entrypoint
macro](https://github.com/solana-labs/solana/blob/7ddf10e602d2ed87a9e3737aa8c32f1db9f909d8/sdk/program/src/entrypoint.rs#L46)
- [BPF Loader deprecated's entrypoint
macro](https://github.com/solana-labs/solana/blob/7ddf10e602d2ed87a9e3737aa8c32f1db9f909d8/sdk/program/src/entrypoint_deprecated.rs#L37)
The program defined instruction processing function that the entrypoint macros
call must be of this form:
```rust
pub type ProcessInstruction =
fn(program_id: &Pubkey, accounts: &[AccountInfo], instruction_data: &[u8]) -> ProgramResult;
```
Refer to [helloworld's use of the
entrypoint](https://github.com/solana-labs/example-helloworld/blob/c1a7247d87cd045f574ed49aec5d160aefc45cf2/src/program-rust/src/lib.rs#L15)
as an example of how things fit together.
### Parameter Deserialization
Each loader provides a helper function that deserializes the program's input
parameters into Rust types. The entrypoint macros automatically calls the
deserialization helper:
- [BPF Loader
deserialization](https://github.com/solana-labs/solana/blob/7ddf10e602d2ed87a9e3737aa8c32f1db9f909d8/sdk/program/src/entrypoint.rs#L104)
- [BPF Loader deprecated
deserialization](https://github.com/solana-labs/solana/blob/7ddf10e602d2ed87a9e3737aa8c32f1db9f909d8/sdk/program/src/entrypoint_deprecated.rs#L56)
Some programs may want to perform deserialization themselves and they can by
providing their own implementation of the [raw entrypoint](#program-entrypoint).
Take note that the provided deserialization functions retain references back to
the serialized byte array for variables that the program is allowed to modify
(lamports, account data). The reason for this is that upon return the loader
will read those modifications so they may be committed. If a program implements
their own deserialization function they need to ensure that any modifications
the program wishes to commit be written back into the input byte array.
Details on how the loader serializes the program inputs can be found in the
[Input Parameter Serialization](overview.md#input-parameter-serialization) docs.
### Data Types
The loader's entrypoint macros call the program defined instruction processor
function with the following parameters:
```rust
program_id: &Pubkey,
accounts: &[AccountInfo],
instruction_data: &[u8]
```
The program id is the public key of the currently executing program.
The accounts is an ordered slice of the accounts referenced by the instruction
and represented as an
[AccountInfo](https://github.com/solana-labs/solana/blob/7ddf10e602d2ed87a9e3737aa8c32f1db9f909d8/sdk/program/src/account_info.rs#L10)
structures. An account's place in the array signifies its meaning, for example,
when transferring lamports an instruction may define the first account as the
source and the second as the destination.
The members of the `AccountInfo` structure are read-only except for `lamports`
and `data`. Both may be modified by the program in accordance with the [runtime
enforcement policy](developing/programming-model/accounts.md#policy). Both of
these members are protected by the Rust `RefCell` construct, so they must be
borrowed to read or write to them. The reason for this is they both point back
to the original input byte array, but there may be multiple entries in the
accounts slice that point to the same account. Using `RefCell` ensures that the
program does not accidentally perform overlapping read/writes to the same
underlying data via multiple `AccountInfo` structures. If a program implements
their own deserialization function care should be taken to handle duplicate
accounts appropriately.
The instruction data is the general purpose byte array from the [instruction's
instruction data](developing/programming-model/transactions.md#instruction-data)
being processed.
## Heap
Rust programs implement the heap directly by defining a custom
[`global_allocator`](https://github.com/solana-labs/solana/blob/8330123861a719cd7a79af0544617896e7f00ce3/sdk/program/src/entrypoint.rs#L50)
Programs may implement their own `global_allocator` based on its specific needs.
Refer to the [custom heap example](#custom-heap) for more information.
## Restrictions
On-chain Rust programs support most of Rust's libstd, libcore, and liballoc, as
well as many 3rd party crates.
There are some limitations since these programs run in a resource-constrained,
single-threaded environment, and must be deterministic:
- No access to
- `rand`
- `std::fs`
- `std::net`
- `std::os`
- `std::future`
- `std::net`
- `std::process`
- `std::sync`
- `std::task`
- `std::thread`
- `std::time`
- Limited access to:
- `std::hash`
- `std::os`
- Bincode is extremely computationally expensive in both cycles and call depth
and should be avoided
- String formatting should be avoided since it is also computationally
expensive.
- No support for `println!`, `print!`, the Solana [logging helpers](#logging)
should be used instead.
- The runtime enforces a limit on the number of instructions a program can
execute during the processing of one instruction. See [computation
budget](developing/programming-model/computation-budget.md) for more
information.
## Logging
Rust's `println!` macro is computationally expensive and not supported. Instead
the helper macro
[`info!`](https://github.com/solana-labs/solana/blob/7ddf10e602d2ed87a9e3737aa8c32f1db9f909d8/sdk/program/src/log.rs#L10)
is provided.
`info!` has two forms:
```rust
info!("A string");
```
or
```rust
info!(0_64, 1_64, 2_64, 3_64, 4_64)
```
Both forms output the results to the program logs. If a program so wishes they
can emulate `println!` by using `format!`:
```rust
info!(&format!("Some varialbe: {:?}", variable));
```
The [debugging](debugging.md#logging) section has more information about working
with program logs.
## Compute Budget
Use the system call
[`sol_log_compute_units()`](https://github.com/solana-labs/solana/blob/d3a3a7548c857f26ec2cb10e270da72d373020ec/sdk/program/src/log.rs#L102)
to log a message containing the remaining number of compute units the program
may consume before execution is halted
See [compute
budget](developing/programming-model/../../../programming-model/compute-budget.md)
for more information.
## ELF Dump
The BPF shared object internals can be dumped to a text file to gain more
insight into a program's composition and what it may be doing at runtime. The
dump will contain both the ELF information as well as a list of all the symbols
and the instructions that implement them. Some of the BPF loader's error log
messages will reference specific instruction numbers where the error occurred.
These references can be looked up in the ELF dump to identify the offending
instruction and its context.
To create a dump file:
```bash
$ cd <program directory>
$ cargo build-bpf --dump
```
## Examples
TODO
### Logging
### Transferring Lamports
### Writing Account Data
### Using a Sysvar
### Custom Heap
### Cross-program Invocations

View File

@@ -0,0 +1,56 @@
---
title: "Examples"
---
## Helloworld
Hello World is a project that demonstrates how to use the Solana Javascript API
and both Rust and C programs to build, deploy, and interact with programs on the
Solana blockchain.
The project comprises of:
- An on-chain hello world program
- A client that can send a "hello" to an account and get back the number of
times "hello" has been sent
## Build and run Hello World program
First fetch the latest version of the example code:
```bash
$ git clone https://github.com/solana-labs/example-helloworld.git
$ cd example-helloworld
```
Next, follow the steps in the git repository's
[README](https://github.com/solana-labs/example-helloworld/blob/master/README.md).
## Break
[Break](https://break.solana.com/) is a React app that gives users a visceral
feeling for just how fast and high-performance the Solana network really is. Can
you _break_ the Solana blockchain? During a 15 second playthough, each click of
a button or keystroke sends a new transaction to the cluster. Smash the keyboard
as fast as you can and watch your transactions get finalized in real time while
the network takes it all in stride!
Break can be played on our Devnet, Testnet and Mainnet Beta networks. Plays are
free on Devnet and Testnet, where the session is funded by a network faucet. On
Mainnet Beta, users pay to play 0.08 SOL per game. The session account can be
funded by a local keystore wallet or by scanning a QR code from Trust Wallet to
transfer the tokens.
[Click here to play Break](https://break.solana.com/)
## Build and run Break locally
First fetch the latest version of the example code:
```bash
$ git clone https://github.com/solana-labs/break.git
$ cd break
```
Next, follow the steps in the git repository's
[README](https://github.com/solana-labs/break/blob/master/README.md).

View File

@@ -0,0 +1,81 @@
---
title: "FAQ"
---
When writing or interacting with Solana programs, there are common questions or
challenges that often come up. Below are resources to help answer these
questions.
If not addressed here, the Solana [#developers](https://discord.gg/RxeGBH)
Discord channel is a great resource.
## `CallDepth` error
This error means that that cross-program invocation exceeded the allowed
invocation call depth.
See [cross-program invocation Call
Depth](developing/programming-model/cpi.md#call-depth)
## `CallDepthExceeded` error
This error means the BPF stack depth was exceeded.
See [call depth](overview.md#call-depth)
## Computational constraints
See [computational
constraints](developing/programming-model/computation-budget.md)
## Float Rust types
See [float support](overview.md#float-support)
## Heap size
See [heap](overview.md#heap)
## InvalidAccountData
This program error can happen for a lot of reasons. Usually, it's caused by
passing an account to the program that the program is not expecting, either in
the wrong position in the instruction or an account not compatible with the
instruction being executed.
An implementation of a program might also cause this error when performing a
cross-program instruction and forgetting to provide the account for the program
that you are calling.
## InvalidInstructionData
This program error can occur while trying to deserialize the instruction, check
that the structure passed in matches exactly the instruction. There may be some
padding between fields. If the program implements the Rust `Pack` trait then try
packing and unpacking the instruction type `T` to determine the exact encoding
the program expects:
https://github.com/solana-labs/solana/blob/v1.4/sdk/program/src/program_pack.rs
## MissingRequiredSignature
Some instructions require the account to be a signer; this error is returned if
an account is expected to be signed but is not.
An implementation of a program might also cause this error when performing a
cross-program invocation that requires a signed program address, but the passed
signer seeds passed to [`invoke_signed`](developing/programming-model/cpi.md)
don't match the signer seeds used to create the program address
[`create_program_address`](developing/programming-model/program-derived-addresses.md).
## `rand` Rust dependency causes compilation failure
See [Rust Project Dependencies](developing-rust.md#project-dependencies)
## Rust restrictions
See [Rust restrictions](developing-rust.md#restrictions)
## Stack size
See [stack](overview.md#stack)

View File

@@ -0,0 +1,178 @@
---
title: "Overview"
---
Developers can write and deploy their own programs to the Solana blockchain.
The [Helloworld example](examples.md#helloworld) is a good starting place to see
how a program is written, built, deployed, and interacted with on-chain.
## Berkley Packet Filter (BPF)
Solana on-chain programs are compiled via the [LLVM compiler
infrastructure](https://llvm.org/) to an [Executable and Linkable Format
(ELF)](https://en.wikipedia.org/wiki/Executable_and_Linkable_Format) containing
a variation of the [Berkley Packet Filter
(BPF)](https://en.wikipedia.org/wiki/Berkeley_Packet_Filter) bytecode.
Because Solana uses the LLVM compiler infrastructure, a program may be written
in any programming language that can target the LLVM's BPF backend. Solana
currently supports writing programs in Rust and C/C++.
BPF provides an efficient [instruction
set](https://github.com/iovisor/bpf-docs/blob/master/eBPF.md) that can be
executed in a interpreted virtual machine or as efficient just-in-time compiled
native instructions.
## Memory map
The virtual address memory map used by Solana BPF programs is fixed and laid out
as follows
- Program code starts at 0x100000000
- Stack data starts at 0x200000000
- Heap data starts at 0x300000000
- Program input parameters start at 0x400000000
The above virtual addresses are start addresses but programs are given access to
a subset of the memory map. The program will panic if it attempts to read or
write to a virtual address that it was not granted access to, and an
`AccessViolation` error will be returned that contains the address and size of
the attempted violation.
## Stack
BPF uses stack frames instead of a variable stack pointer. Each stack frame is
4KB in size. If a program violates that stack frame size, the compiler will
report the overrun as a warning. The reason a warning is reported rather than an
error is because some dependent crates may include functionality that violates
the stack frame restrictions even if the program doesn't use that functionality.
If the program violates the stack size at runtime, an `AccessViolation` error
will be reported.
BPF stack frames occupy a virtual address range starting at 0x200000000.
## Call Depth
Programs are constrained to run quickly, and to facilitate this, the program's
call stack is limited to a max depth of 64 frames.
## Heap
Programs have access to a runtime heap either directly in C or via the Rust
`alloc` APIs. To facilitate fast allocations, a simple 32KB bump heap is
utilized. The heap does not support `free` or `realloc` so use it wisely.
Internally, programs have access to the 32KB memory region starting at virtual
address 0x300000000 and may implement a custom heap based on the the program's
specific needs.
- [Rust program heap usage](developing-rust.md#heap)
- [C program heap usage](developing-c.md#heap)
## Float Support
Programs support a limited subset of Rust's float operations, though they are
highly discouraged due to the overhead involved. If a program attempts to use a
float operation that is not supported, the runtime will report an unresolved
symbol error.
## Static Writable Data
Program shared objects do not support writable shared data. Programs are shared
between multiple parallel executions using the same shared read-only code and
data. This means that developers should not include any static writable or
global variables in programs. In the future a copy-on-write mechanism could be
added to support writable data.
## Signed division
The BPF instruction set does not support [signed
division](https://www.kernel.org/doc/html/latest/bpf/bpf_design_QA.html#q-why-there-is-no-bpf-sdiv-for-signed-divide-operation).
Adding a signed division instruction is a consideration.
## Loaders
Programs are deployed with and executed by runtime loaders, currently there are
two supported loaders [BPF
Loader](https://github.com/solana-labs/solana/blob/7ddf10e602d2ed87a9e3737aa8c32f1db9f909d8/sdk/program/src/bpf_loader.rs#L17)
and [BPF loader
deprecated](https://github.com/solana-labs/solana/blob/7ddf10e602d2ed87a9e3737aa8c32f1db9f909d8/sdk/program/src/bpf_loader_deprecated.rs#L14)
Loaders may support different application binary interfaces so developers must
write their programs for and deploy them to the same loader. If a program
written for one loader is deployed to a different one the result is usually a
`AccessViolation` error due to mismatched deserialization of the program's input
parameters.
For all practical purposes program should always be written to target the latest
BPF loader and the latest loader is the default for the command-line interface
and the javascript APIs.
For language specific information about implementing a program for a particular
loader see:
- [Rust program entrypoints](developing-rust.md#program-entrypoint)
- [C program entrypoints](developing-c.md#program-entrypoint)
### Deployment
BPF program deployment is the process of uploading a BPF shared object into a
program account's data and marking the account executable. A client breaks the
BPF shared object into smaller pieces and sends them as the instruction data of
[`Write`](https://github.com/solana-labs/solana/blob/bc7133d7526a041d1aaee807b80922baa89b6f90/sdk/program/src/loader_instruction.rs#L13)
instructions to the loader where loader writes that data into the program's
account data. Once all the pieces are received the client sends a
[`Finalize`](https://github.com/solana-labs/solana/blob/bc7133d7526a041d1aaee807b80922baa89b6f90/sdk/program/src/loader_instruction.rs#L30)
instruction to the loader, the loader then validates that the BPF data is valid
and marks the program account as _executable_. Once the program account is
marked executable, subsequent transactions may issue instructions for that
program to process.
When an instruction is directed at an executable BPF program the loader
configures the program's execution environment, serializes the program's input
parameters, calls the program's entrypoint, and reports any errors encountered.
For further information see [deploying](deploying.md)
### Input Parameter Serialization
BPF loaders serialize the program input parameters into a byte array that is
then passed to the program's entrypoint where the program is responsible for
deserializing it on-chain. One of the changes between the deprecated loader and
the current loader is that the input parameters are serialized in a way that
results in various parameters falling on aligned offsets within the aligned byte
array. This allows deserialization implementations to directly reference the
byte array and provide aligned pointers to the program.
The current loader serializes the program input parameters as follows (all
encoding is little endian):
- 8 byte unsigned number of accounts
- For each account
- 1 byte indicating if this is a duplicate account, if it is a duplicate then
the value is 0, otherwise contains the index of the account it is a
duplicate of
- 7 bytes of padding
- if not duplicate
- 1 byte padding
- 1 byte boolean, true if account is a signer
- 1 byte boolean, true if account is writable
- 1 byte boolean, true if account is executable
- 4 bytes of padding
- 32 bytes of the account public key
- 32 bytes of the account's owner public key
- 8 byte unsigned number of lamports owned by the account
- 8 bytes unsigned number of bytes of account data
- x bytes of account data
- 10k bytes of padding, used for realloc
- enough padding to align the offset to 8 bytes.
- 8 bytes rent epoch
- 8 bytes of unsigned number of instruction data
- x bytes of instruction data
- 32 bytes of the program id
For language specific information about serialization see:
- [Rust program parameter
deserialization](developing-rust.md#parameter-deserialization)
- [C program parameter
deserialization](developing-c.md#parameter-deserialization)

View File

@@ -0,0 +1,210 @@
---
title: "Accounts"
---
## 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"](apps/rent.md) to stay there. Each validator periodically scans all
accounts and collects rent. Any account that drops to zero lamports is purged.
In the same way that a Linux user uses a path to look up a file, a Solana client
uses an _address_ to look up an account. The address is usually a 256-bit public
key.
## Signers
Transactions may include digital [signatures](terminology.md#signature)
corresponding to the accounts' public keys referenced by the transaction. When a
corresponding digital signature is present it signifies that the holder of the
account's private key signed and thus "authorized" the transaction and the
account is then referred to as a _signer_. Whether an account is a signer or not
is communicated to the program as part of the account's metadata. Programs can
then use that information to make authority decisions.
## Read-only
Transactions can mark some accounts as _read-only accounts_. The runtime permits
read-only accounts to be read concurrently by multiple programs. If a program
attempts to modify a read-only account, the transaction is rejected by the
runtime.
## Executable
If an account is marked "executable" in its metadata, it can be used by a
_loader_ to run programs. For example, a BPF-compiled program is marked
executable by the BPF loader during deployment once the loader has determined
that the BPF bytecode in the account's data is valid. No program is allowed to
modify the contents of an executable account once deployed and executable mark
is permanent.
## Creating
To create an account a client generates a _keypair_ and registers its public key
using the `SystemProgram::CreateAccount` instruction with preallocated a fixed
storage size in bytes. The current maximum size of an account's data is 10
megabytes.
An account address can be any arbitrary 256 bit value, and there are mechanisms
for advanced users to create derived addresses
(`SystemProgram::CreateAccountWithSeed`,
[`Pubkey::CreateProgramAddress`](program-derived-addresses.md)).
Accounts that have never been created via the system program can also be passed
to programs. When an instruction references an account that hasn't been
previously created the program will be passed an account that is owned by the
system program, has zero lamports, and zero data. But, the account will reflect
whether it is a signer of the transaction or not and therefore can be used as an
authority. Authorities in this context convey to the program that the holder of
the private key associated with the account's public key signed the transaction.
The account's public key may be known to the program or recorded in another
account and signify some kind of ownership or authority over an asset or
operation the program controls or performs.
## Ownership and Assignment to Programs
A 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.
## Runtime Capability of Programs
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 accounts owned by a given program can be
regarded as a key-value store where a 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 the access policy was not violated. If a program
violates the policy, the runtime discards all account changes made by all
instructions in the transaction and marks the transaction as failed.
### Policy
After a program has processed an instruction the runtime verifies that the
program only performed operations it was permitted to, and that the results
adhere to the runtime policy.
The policy is as follows:
- Only the owner of the account may change owner.
- And only if the account is writable.
- And only if the data is zero-initialized or empty.
- An account not assigned to the program cannot have its balance decrease.
- The balance of read-only and executable accounts may not change.
- Only the system program can change the size of the data and only if the system
program owns the account.
- Only the owner may change account data.
- And if the account is writable.
- And if the account is not executable.
- Executable is one-way (false->true) and only the account owner may set it.
- No one modification to the rent_epoch associated with this account.
## Rent
Keeping accounts alive on Solana incurs a storage cost called _rent_ because the
cluster must actively maintain the data to process any future transactions on
it. This is different from Bitcoin and Ethereum, where storing accounts doesn't
incur any costs.
The rent is debited from an account's balance by the runtime upon the first
access (including the initial account creation) in the current epoch by
transactions or once per an epoch if there are no transactions. The fee is
currently a fixed rate, measured in bytes-times-epochs. The fee may change in
the future.
For the sake of simple rent calculation, rent is always collected for a single,
full epoch. Rent is not pro-rated, meaning there are neither fees nor refunds
for partial epochs. This means that, on account creation, the first rent
collected isn't for the current partial epoch, but collected up front for the
next full epoch. Subsequent rent collections are for further future epochs. On
the other end, if the balance of an already-rent-collected account drops below
another rent fee mid-epoch, the account will continue to exist through the
current epoch and be purged immediately at the start of the upcoming epoch.
Accounts can be exempt from paying rent if they maintain a minimum balance. This
rent-exemption is described below.
### Calculation of rent
Note: The rent rate can change in the future.
As of writing, the fixed rent fee is 19.055441478439427 lamports per byte-epoch
on the testnet and mainnet-beta clusters. An [epoch](../terminology.md#epoch) is
targeted to be 2 days (For devnet, the rent fee is 0.3608183131797095 lamports
per byte-epoch with its 54m36s-long epoch).
This value is calculated to target 0.01 SOL per mebibyte-day (exactly matching
to 3.56 SOL per mebibyte-year):
```text
Rent fee: 19.055441478439427 = 10_000_000 (0.01 SOL) * 365(approx. day in a year) / (1024 * 1024)(1 MiB) / (365.25/2)(epochs in 1 year)
```
And rent calculation is done with the `f64` precision and the final result is
truncated to `u64` in lamports.
The rent calculation includes account metadata (address, owner, lamports, etc)
in the size of an account. Therefore the smallest an account can be for rent
calculations is 128 bytes.
For example, an account is created with the initial transfer of 10,000 lamports
and no additional data. Rent is immediately debited from it on creation,
resulting in a balance of 7,561 lamports:
```text
Rent: 2,439 = 19.055441478439427 (rent rate) * 128 bytes (minimum account size) * 1 (epoch)
Account Balance: 7,561 = 10,000 (transfered lamports) - 2,439 (this account's rent fee for an epoch)
```
The account balance will be reduced to 5,122 lamports at the next epoch even if
there is no activity:
```text
Account Balance: 5,122 = 7,561 (current balance) - 2,439 (this account's rent fee for an epoch)
```
Accordingly, a minimum-size account will be immediately removed after creation
if the transferred lamports are less than or equal to 2,439.
### Rent exemption
Alternatively, an account can be made entirely exempt from rent collection by
depositing at least 2 years-worth of rent. This is checked every time an
account's balance is reduced and rent is immediately debited once the balance
goes below the minimum amount.
Program executable accounts are required by the runtime to be rent-exempt to
avoid being purged.
Note: Use the [`getMinimumBalanceForRentExemption` RPC
endpoint](jsonrpc-api.md#getminimumbalanceforrentexemption) to calculate the
minimum balance for a particular account size. The following calculation is
illustrative only.
For example, a program executable with the size of 15,000 bytes requires a
balance of 105,290,880 lamports (=~ 0.105 SOL) to be rent-exempt:
```text
105,290,880 = 19.055441478439427 (fee rate) * (128 + 15_000)(account size including metadata) * ((365.25/2) * 2)(epochs in 2 years)
```

View File

@@ -0,0 +1,61 @@
---
title: "Compute budget"
---
To prevent a program from abusing computation resources each instruction in a
transaction is given a compute budget. The budget consists of computation units
that are consumed as the program performs various operations and bounds that the
program may not exceed. When the program consumes its entire budget or exceeds
a bound then the runtime halts the program and returns an error.
The following operations incur a compute cost:
- Executing BPF instructions
- Calling system calls
- logging
- creating program addresses
- cross-program invocations
- ...
For cross-program invocations the programs invoked inherit the budget of their
parent. If an invoked program consume the budget or exceeds a bound the entire
invocation chain and the parent are halted.
The current [compute
budget](https://github.com/solana-labs/solana/blob/d3a3a7548c857f26ec2cb10e270da72d373020ec/sdk/src/process_instruction.rs#L65)
can be found in the Solana SDK.
For example, if the current budget is:
```rust
max_units: 200,000,
log_units: 100,
log_u64_units: 100,
create_program address units: 1500,
invoke_units: 1000,
max_invoke_depth: 4,
max_call_depth: 64,
stack_frame_size: 4096,
log_pubkey_units: 100,
```
Then the program
- Could execute 200,000 BPF instructions if it does nothing else
- Could log 2,000 log messages
- Can not exceed 4k of stack usage
- Can not exceed a BPF call depth of 64
- Cannot exceed 4 levels of cross-program invocations.
Since the compute budget is consumed incrementally as the program executes the
total budget consumption will be a combination of the various costs of the
operations it performs.
At runtime a program may log how much of the compute budget remains. See
[debugging](developing/deployed-programs/debugging.md#monitoring-compute-budget-consumption)
for more information.
The budget values are conditional on feature enablement, take a look the compute
budget's
[new](https://github.com/solana-labs/solana/blob/d3a3a7548c857f26ec2cb10e270da72d373020ec/sdk/src/process_instruction.rs#L97)
function to find out how the budget is constructed. An understanding of how
[features](runtime-features.md) work and what features are enabled on the
cluster being used are required to determine the current budget's values.

View File

@@ -0,0 +1,150 @@
---
title: Cross-Program Invocation
---
## Problem
In today's implementation, a client can create a transaction that modifies two
accounts, each owned by a separate on-chain program:
```rust,ignore
let message = Message::new(vec![
token_instruction::pay(&alice_pubkey),
acme_instruction::launch_missiles(&bob_pubkey),
]);
client.send_and_confirm_message(&[&alice_keypair, &bob_keypair], &message);
```
However, the current implementation does not allow the `acme` program to
conveniently invoke `token` instructions on the client's behalf:
```rust,ignore
let message = Message::new(vec![
acme_instruction::pay_and_launch_missiles(&alice_pubkey, &bob_pubkey),
]);
client.send_and_confirm_message(&[&alice_keypair, &bob_keypair], &message);
```
Currently, there is no way to create instruction `pay_and_launch_missiles` that
executes `token_instruction::pay` from the `acme` program. A possible workaround
is to extend the `acme` program with the implementation of the `token` program
and create `token` accounts with `ACME_PROGRAM_ID`, which the `acme` program is
permitted to modify. With that workaround, `acme` can modify token-like accounts
created by the `acme` program, but not token accounts created by the `token`
program.
## Solution
The goal of this design is to modify Solana's runtime such that an on-chain
program can invoke an instruction from another program.
Given two on-chain programs `token` and `acme`, each implementing instructions
`pay()` and `launch_missiles()` respectively, we would ideally like to implement
the `acme` module with a call to a function defined in the `token` module:
```rust,ignore
mod acme {
use token;
fn launch_missiles(keyed_accounts: &[KeyedAccount]) -> Result<()> {
...
}
fn pay_and_launch_missiles(keyed_accounts: &[KeyedAccount]) -> Result<()> {
token::pay(&keyed_accounts[1..])?;
launch_missiles(keyed_accounts)?;
}
```
The above code would require that the `token` crate be dynamically linked so
that a custom linker could intercept calls and validate accesses to
`keyed_accounts`. Even though the client intends to modify both `token` and
`acme` accounts, only `token` program is permitted to modify the `token`
account, and only the `acme` program is allowed to modify the `acme` account.
Backing off from that ideal direct cross-program call, a slightly more verbose
solution is to allow `acme` to invoke `token` by issuing a token instruction via
the runtime.
```rust,ignore
mod acme {
use token_instruction;
fn launch_missiles(keyed_accounts: &[KeyedAccount]) -> Result<()> {
...
}
fn pay_and_launch_missiles(keyed_accounts: &[KeyedAccount]) -> Result<()> {
let alice_pubkey = keyed_accounts[1].key;
let instruction = token_instruction::pay(&alice_pubkey);
invoke(&instruction, accounts)?;
launch_missiles(keyed_accounts)?;
}
```
`invoke()` is built into Solana's runtime and is responsible for routing the
given instruction to the `token` program via the instruction's `program_id`
field.
Before invoking `pay()`, the runtime must ensure that `acme` didn't modify any
accounts owned by `token`. It does this by applying the runtime's policy to the
current state of the accounts at the time `acme` calls `invoke` vs. the initial
state of the accounts at the beginning of the `acme`'s instruction. After
`pay()` completes, the runtime must again ensure that `token` didn't modify any
accounts owned by `acme` by again applying the runtime's policy, but this time
with the `token` program ID. Lastly, after `pay_and_launch_missiles()`
completes, the runtime must apply the runtime policy one more time, where it
normally would, but using all updated `pre_*` variables. If executing
`pay_and_launch_missiles()` up to `pay()` made no invalid account changes,
`pay()` made no invalid changes, and executing from `pay()` until
`pay_and_launch_missiles()` returns made no invalid changes, then the runtime
can transitively assume `pay_and_launch_missiles()` as whole made no invalid
account changes, and therefore commit all these account modifications.
### Instructions that require privileges
The runtime uses the privileges granted to the caller program to determine what
privileges can be extended to the callee. Privileges in this context refer to
signers and writable accounts. For example, if the instruction the caller is
processing contains a signer or writable account, then the caller can invoke an
instruction that also contains that signer and/or writable account.
This privilege extension relies on the fact that programs are immutable. In the
case of the `acme` program, the runtime can safely treat the transaction's
signature as a signature of a `token` instruction. When the runtime sees the
`token` instruction references `alice_pubkey`, it looks up the key in the `acme`
instruction to see if that key corresponds to a signed account. In this case, it
does and thereby authorizes the `token` program to modify Alice's account.
### Program signed accounts
Programs can issue instructions that contain signed accounts that were not
signed in the original transaction by using [Program derived
addresses](program-derived-addresses.md).
To sign an account with program derived addresses, a program may
`invoke_signed()`.
```rust,ignore
invoke_signed(
&instruction,
accounts,
&[&["First addresses seed"],
&["Second addresses first seed", "Second addresses second seed"]],
)?;
```
### Call Depth
Cross-program invocations allow programs to invoke other programs directly but
the depth is constrained currently to 4.
### Reentrancy
Reentrancy is currently limited to direct self recursion capped at a fixed
depth. This restriction prevents situations where a program might invoke another
from an intermediary state without the knowledge that it might later be called
back into. Direct recursion gives the program full control of its state at the
point that it gets called back.

View File

@@ -0,0 +1,15 @@
---
title: "Overview"
---
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 transferred.
Instructions are executed sequentially and atomically for each transaction. If
any instruction is invalid, all account changes in the transaction are
discarded.
To start developing immediately you can build, deploy, and run one of the
[examples](developing/deployed-programs/examples.md).

View File

@@ -0,0 +1,159 @@
---
title: Program Derived Addresses
---
## Problem
Programs cannot generate signatures when issuing instructions to other programs
as defined in the [Cross-Program Invocations](cross-program-invocation.md)
design.
The lack of programmatic signature generation limits the kinds of programs that
can be implemented in Solana. A program may be given the authority over an
account and later want to transfer that authority to another. This is impossible
today because the program cannot act as the signer in the transaction that gives
authority.
For example, if two users want to make a wager on the outcome of a game in
Solana, they must each transfer their wager's assets to some intermediary that
will honor their agreement. Currently, there is no way to implement this
intermediary as a program in Solana because the intermediary program cannot
transfer the assets to the winner.
This capability is necessary for many DeFi applications since they require
assets to be transferred to an escrow agent until some event occurs that
determines the new owner.
- Decentralized Exchanges that transfer assets between matching bid and ask
orders.
- Auctions that transfer assets to the winner.
- Games or prediction markets that collect and redistribute prizes to the
winners.
## Solution
The key to the design is two-fold:
1. Allow programs to control specific addresses, called program addresses, in
such a way that no external user can generate valid transactions with
signatures for those addresses.
2. Allow programs to programmatically sign for programa addresses that are
present in instructions invoked via [Cross-Program
Invocations](cross-program-invocation.md).
Given the two conditions, users can securely transfer or assign the authority of
on-chain assets to program addresses and the program can then assign that
authority elsewhere at its discretion.
### Private keys for program addresses
A Program address does not lie on the ed25519 curve and therefore has no valid
private key associated with it, and thus generating a signature for it is
impossible. While it has no private key of its own, it can be used by a program
to issue an instruction that includes the Program address as a signer.
### Hash-based generated program addresses
Program addresses are deterministically derived from a collection of seeds and a
program id using a 256-bit pre-image resistant hash function. Program address
must not lie on the ed25519 curve to ensure there is no associated private key.
During generation an error will be returned if the address is found to lie on
the curve. There is about a 50/50 change of this happening for a given
collection of seeds and program id. If this occurs a different set of seeds or
a seed bump (additional 8 bit seed) can be used to find a valid program address
off the curve.
Deterministic program addresses for programs follow a similar derivation path as
Accounts created with `SystemInstruction::CreateAccountWithSeed` which is
implemented with `system_instruction::create_address_with_seed`.
For reference that implementation is as follows:
```rust,ignore
pub fn create_address_with_seed(
base: &Pubkey,
seed: &str,
program_id: &Pubkey,
) -> Result<Pubkey, SystemError> {
if seed.len() > MAX_ADDRESS_SEED_LEN {
return Err(SystemError::MaxSeedLengthExceeded);
}
Ok(Pubkey::new(
hashv(&[base.as_ref(), seed.as_ref(), program_id.as_ref()]).as_ref(),
))
}
```
Programs can deterministically derive any number of addresses by using seeds.
These seeds can symbolically identify how the addresses are used.
From `Pubkey`::
```rust,ignore
/// Generate a derived program address
/// * seeds, symbolic keywords used to derive the key
/// * program_id, program that the address is derived for
pub fn create_program_address(
seeds: &[&[u8]],
program_id: &Pubkey,
) -> Result<Pubkey, PubkeyError>
```
### Using program addresses
Clients can use the `create_program_address` function to generate a destination
address.
```rust,ignore
// deterministically derive the escrow key
let escrow_pubkey = create_program_address(&[&["escrow"]], &escrow_program_id);
// construct a transfer message using that key
let message = Message::new(vec![
token_instruction::transfer(&alice_pubkey, &escrow_pubkey, 1),
]);
// process the message which transfer one 1 token to the escrow
client.send_and_confirm_message(&[&alice_keypair], &message);
```
Programs can use the same function to generate the same address. In the function
below the program issues a `token_instruction::transfer` from a program address
as if it had the private key to sign the transaction.
```rust,ignore
fn transfer_one_token_from_escrow(
program_id: &Pubkey,
keyed_accounts: &[KeyedAccount]
) -> Result<()> {
// User supplies the destination
let alice_pubkey = keyed_accounts[1].unsigned_key();
// Deterministically derive the escrow pubkey.
let escrow_pubkey = create_program_address(&[&["escrow"]], program_id);
// Create the transfer instruction
let instruction = token_instruction::transfer(&escrow_pubkey, &alice_pubkey, 1);
// The runtime deterministically derives the key from the currently
// executing program ID and the supplied keywords.
// If the derived address matches a key marked as signed in the instruction
// then that key is accepted as signed.
invoke_signed(&instruction, &[&["escrow"]])?
}
```
### Instructions that require signers
The addresses generated with `create_program_address` are indistinguishable from
any other public key. The only way for the runtime to verify that the address
belongs to a program is for the program to supply the seeds used to generate the
address.
The runtime will internally call `create_program_address`, and compare the
result against the addresses supplied in the instruction.

View File

@@ -0,0 +1,26 @@
---
title: "Runtime Features"
---
As Solana evolves, new features or patches may be introduced that changes the
behavior of the cluster and how programs run. Changes in behavior must be
coordinated between the various nodes of the cluster, if nodes do not coordinate
then these changes can result in a break-down of consensus. Solana supports a
mechanism called runtime features to facilitate the smooth adoption of changes.
Runtime features are epoch coordinated events where one or more behavior changes
to the cluster will occur. New changes to Solana that will change behavior are
wrapped with feature gates and disabled by default. The Solana tools are then
used to activate a feature, which marks it pending, once marked pending the
feature will be activated at the next epoch.
To determine which features are activated use the [Solana command-line
tools](cli/install-solana-cli-tools.md):
```bash
solana feature status
```
If you encounter problems first ensure that the Solana tools version you are
using match the version returned by `solana cluster-version`. If they do not
match [install the correct tool suite](cli/install-solana-cli-tools.md).

View File

@@ -0,0 +1,92 @@
---
title: secp256k1 builtin instructions
---
## Problem
Performing multiple secp256k1 pubkey recovery operations (ecrecover) in BPF
would exceed the transction bpf instruction limit and even if the limit is
increased it would take a long time to process. ecrecover is an ethereum
instruction which takes a signature and message and recovers a publickey, a
comparison to that public key can thus verify that the signature is valid.
Since there needs to be 10-20 signatures in the transaction as well as the
signing data which is on the order of 500 bytes, transaction space is a concern.
But also having more concentrated similar work should provide for easier
optimization.
## Solution
Add a new builtin instruction which takes in as the first byte a count of the
following struct serialized in the instruction data:
```
struct Secp256k1SignatureOffsets {
secp_signature_key_offset: u16, // offset to [signature,recovery_id,etherum_address] of 64+1+20 bytes
secp_signature_instruction_index: u8, // instruction index to find data
secp_pubkey_offset: u16, // offset to [signature,recovery_id] of 64+1 bytes
secp_signature_instruction_index: u8, // instruction index to find data
secp_message_data_offset: u16, // offset to start of message data
secp_message_data_size: u16, // size of message data
secp_message_instruction_index: u8, // index of instruction data to get message data
}
```
Pseudo code of the operation:
```
process_instruction() {
for i in 0..count {
// i'th index values referenced:
instructions = &transaction.message().instructions
signature = instructions[secp_signature_instruction_index].data[secp_signature_offset..secp_signature_offset + 64]
recovery_id = instructions[secp_signature_instruction_index].data[secp_signature_offset + 64]
ref_eth_pubkey = instructions[secp_pubkey_instruction_index].data[secp_pubkey_offset..secp_pubkey_offset + 32]
message_hash = keccak256(instructions[secp_message_instruction_index].data[secp_message_data_offset..secp_message_data_offset + secp_message_data_size])
pubkey = ecrecover(signature, recovery_id, message_hash)
eth_pubkey = keccak256(pubkey[1..])[12..]
if eth_pubkey != ref_eth_pubkey {
return Error
}
}
return Success
}
```
This allows the user to specify any instruction data in the transaction for
signature and message data. By specifying a special instructions sysvar, one can
also receive data from the transaction itself.
Cost of the transaction will count the number of signatures to verify multiplied
by the signature cost verify multiplier.
## Optimization notes
The operation will have to take place after (at least partial) deserialization,
but all inputs come from the transaction data itself, this allows it to be
relatively easy to execute in parallel to transaction processing and PoH
verification.
## Other solutions
* Instruction available as CPI such that the program can call as desired or a
syscall which can operate on the instruction inline.
- Could be harder to optimize given that it generally either requires bpf
program scan to determine the inputs to the operation, or the
implementation needs to just wait until the program hits the operation in
bpf processing to evaluate it.
- Vector version of the operation could allow for somewhat efficient simd/gpu
execution. For most efficient though, batching with other instructions in
the pipeline would be ideal.
- Pros - Nicer interface for the user.
* Async execution environment inside bpf
- Might be hard to optimize for devices like gpus which cannot queue work for
itself easily
- Might be easier to optimize on cpu since ordering can be more explicit
* All inputs have to come from the instruction
- Pros - easier to optimize, data is already sent to the GPU for instance for
regular sigverify. Probably still need to wait for deserialize though.
- Cons - ask for pubkeys outside the transaction data itself since they would
not be stored on the transaction sending client, and larger transaction
size.

View File

@@ -0,0 +1,121 @@
---
title: Sysvar Cluster Data
---
Solana exposes a variety of cluster state data to programs via
[`sysvar`](terminology.md#sysvar) accounts. These accounts are populated at
known addresses published along with the account layouts in the
[`solana-program`
crate](https://docs.rs/solana-program/VERSION_FOR_DOCS_RS/solana_program/sysvar/index.html),
and outlined below.
To include sysvar data in program operations, pass the sysvar account address in
the list of accounts in a transaction. The account can be read in your
instruction processor like any other account. Access to sysvars accounts ßis
always *readonly*.
## Clock
The Clock sysvar contains data on cluster time, including the current slot,
epoch, and estimated wall-clock Unix timestamp. It is updated every slot.
- Address: `SysvarC1ock11111111111111111111111111111111`
- Layout: [Clock](https://docs.rs/solana-program/VERSION_FOR_DOCS_RS/solana_program/clock/struct.Clock.html)
- Fields:
- `slot`: the current slot
- `epoch_start_timestamp`: the Unix timestamp of the first slot in this epoch. In the first slot of an epoch, this timestamp is identical to the `unix_timestamp` (below).
- `epoch`: the current epoch
- `leader_schedule_epoch`: the most recent epoch for which the leader schedule has already been generated
- `unix_timestamp`: the Unix timestamp of this slot.
Each slot has an estimated duration based on Proof of History. But in reality,
slots may elapse faster and slower than this estimate. As a result, the Unix
timestamp of a slot is generated based on oracle input from voting validators.
This timestamp is calculated as the stake-weighted median of timestamp
estimates provided by votes, bounded by the expected time elapsed since the
start of the epoch.
More explicitly: for each slot, the most recent vote timestamp provided by
each validator is used to generate a timestamp estimate for the current slot
(the elapsed slots since the vote timestamp are assumed to be
Bank::ns_per_slot). Each timestamp estimate is associated with the stake
delegated to that vote account to create a distribution of timestamps by
stake. The median timestamp is used as the `unix_timestamp`, unless the
elapsed time since the `epoch_start_timestamp` has deviated from the expected
elapsed time by more than 25%.
## EpochSchedule
The EpochSchedule sysvar contains epoch scheduling constants that are set in
genesis, and enables calculating the number of slots in a given epoch, the epoch
for a given slot, etc. (Note: the epoch schedule is distinct from the [`leader
schedule`](terminology.md#leader-schedule))
- Address: `SysvarEpochSchedu1e111111111111111111111111`
- Layout:
[EpochSchedule](https://docs.rs/solana-program/VERSION_FOR_DOCS_RS/solana_program/epoch_schedule/struct.EpochSchedule.html)
## Fees
The Fees sysvar contains the fee calculator for the current slot. It is updated
every slot, based on the fee-rate governor.
- Address: `SysvarFees111111111111111111111111111111111`
- Layout:
[Fees](https://docs.rs/solana-program/VERSION_FOR_DOCS_RS/solana_program/sysvar/fees/struct.Fees.html)
## Instructions
The Instructions sysvar contains the serialized instructions in a Message while
that Message is being processed. This allows program instructions to reference
other instructions in the same transaction. Read more information on
[instruction introspection](implemented-proposals/instruction_introspection.md).
- Address: `Sysvar1nstructions1111111111111111111111111`
- Layout:
[Instructions](https://docs.rs/solana-program/VERSION_FOR_DOCS_RS/solana_program/sysvar/instructions/type.Instructions.html)
## RecentBlockhashes
The RecentBlockhashes sysvar contains the active recent blockhashes as well as
their associated fee calculators. It is updated every slot.
- Address: `SysvarRecentB1ockHashes11111111111111111111`
- Layout:
[RecentBlockhashes](https://docs.rs/solana-program/VERSION_FOR_DOCS_RS/solana_program/sysvar/recent_blockhashes/struct.RecentBlockhashes.html)
## Rent
The Rent sysvar contains the rental rate. Currently, the rate is static and set
in genesis. The Rent burn percentage is modified by manual feature activation.
- Address: `SysvarRent111111111111111111111111111111111`
- Layout:
[Rent](https://docs.rs/solana-program/VERSION_FOR_DOCS_RS/solana_program/rent/struct.Rent.html)
## SlotHashes
The SlotHashes sysvar contains the most recent hashes of the slot's parent
banks. It is updated every slot.
- Address: `SysvarS1otHashes111111111111111111111111111`
- Layout:
[SlotHashes](https://docs.rs/solana-program/VERSION_FOR_DOCS_RS/solana_program/slot_hashes/struct.SlotHashes.html)
## SlotHistory
The SlotHistory sysvar contains a bitvector of slots present over the last
epoch. It is updated every slot.
- Address: `SysvarS1otHistory11111111111111111111111111`
- Layout:
[SlotHistory](https://docs.rs/solana-program/VERSION_FOR_DOCS_RS/solana_program/slot_history/struct.SlotHistory.html)
## StakeHistory
The StakeHistory sysvar contains the history of cluster-wide stake activations
and de-activations per epoch. It is updated at the start of every epoch.
- Address: `SysvarStakeHistory1111111111111111111111111`
- Layout:
[StakeHistory](https://docs.rs/solana-program/VERSION_FOR_DOCS_RS/solana_program/stake_history/struct.StakeHistory.html)

View File

@@ -0,0 +1,111 @@
---
title: "Transactions"
---
Program execution begins with a [transaction](terminology.md#transaction) being
submitted to the cluster. The Solana runtime will execute a program to process
each of the [instructions](terminology.md#instruction) contained in the
transaction, in order, and atomically.
See [Anatomy of a Transaction](transaction.md) for more information about how a
transaction is encoded.
## Instructions
Each [instruction](terminology.md#instruction) specifies a single program, a
subset of the transaction's accounts that should be passed to the program, and a
data byte array that is passed to the program. The program interprets the data
array and operates on the accounts specified by the instructions. The program
can return successfully, or with an error code. An error return causes the
entire transaction to fail immediately.
Program's typically provide helper functions to construct instruction they
support. For example, the system program provides the following Rust helper to
construct a
[`SystemInstruction::CreateAccount`](https://github.com/solana-labs/solana/blob/6606590b8132e56dab9e60b3f7d20ba7412a736c/sdk/program/src/system_instruction.rs#L63)
instruction:
```rust
pub fn create_account(
from_pubkey: &Pubkey,
to_pubkey: &Pubkey,
lamports: u64,
space: u64,
owner: &Pubkey,
) -> Instruction {
let account_metas = vec![
AccountMeta::new(*from_pubkey, true),
AccountMeta::new(*to_pubkey, true),
];
Instruction::new(
system_program::id(),
&SystemInstruction::CreateAccount {
lamports,
space,
owner: *owner,
},
account_metas,
)
}
```
Which can be found here:
https://github.com/solana-labs/solana/blob/6606590b8132e56dab9e60b3f7d20ba7412a736c/sdk/program/src/system_instruction.rs#L220
### Program ID
The instruction's [program id](terminology.md#program-id) specifies which
program will process this instruction. The program's account data contains
information about how the runtime should execute the program, in the case of BPF
programs, the account data holds the BPF bytecode. Program accounts are marked
as executable once they are successfully deployed. The runtime will reject
transactions that specify programs that are not executable.
### Accounts
The accounts referenced by an instruction represent on-chain state and serve as
both the inputs and outputs of a program. More information about Accounts can be
found in the [Accounts](accounts.md) section.
### Instruction data
Each instruction caries a general purpose byte array that is passed to the
program along with the accounts. The contents of the instruction data is program
specific and typically used to convey what operations the program should
perform, and any additional information those operations may need above and
beyond what the accounts contain.
Programs are free to specify how information is encoded into the instruction
data byte array. The choice of how data is encoded should take into account the
overhead of decoding since that step is performed by the program on-chain. It's
been observed that some common encodings (Rust's bincode for example) are very
inefficient.
The [Solana Program Library's Token
program](https://github.com/solana-labs/solana-program-library/tree/master/token)
gives one example of how instruction data can be encoded efficiently, but note
that this method only supports fixed sized types. Token utilizes the
[Pack](https://github.com/solana-labs/solana/blob/master/sdk/program/src/program_pack.rs)
trait to encode/decode instruction data for both token instructions as well as
token account states.
## Signatures
Each transaction explicitly lists all account public keys referenced by the
transaction's instructions. A subset of those public keys are each accompanied
by a transaction signature. Those signatures signal on-chain programs that the
account holder has authorized the transaction. Typically, the program uses the
authorization to permit debiting the account or modifying its data. More
information about how the authorization is communicated to a program can be
found in [Accounts](accounts.md#signers)
## Recent Blockhash
A transaction includes a recent [blockhash](terminology.md#blockhash) to prevent
duplication and to give transactions lifetimes. Any transaction that is
completely identical to a previous one is rejected, so adding a newer blockhash
allows multiple transactions to repeat the exact same action. Transactions also
have lifetimes that are defined by the blockhash, as any transaction whose
blockhash is too old will be rejected.