Flesh out development docs (#13318)
* flesh out development docs * nits
This commit is contained in:
141
docs/src/developing/backwards-compatibility.md
Normal file
141
docs/src/developing/backwards-compatibility.md
Normal 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.
|
63
docs/src/developing/builtin-programs.md
Normal file
63
docs/src/developing/builtin-programs.md
Normal 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.
|
5
docs/src/developing/clients/javascript-api.md
Normal file
5
docs/src/developing/clients/javascript-api.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
title: Web3 JavaScript API
|
||||
---
|
||||
|
||||
See [solana-web3](https://solana-labs.github.io/solana-web3.js/).
|
3402
docs/src/developing/clients/jsonrpc-api.md
Normal file
3402
docs/src/developing/clients/jsonrpc-api.md
Normal file
File diff suppressed because it is too large
Load Diff
118
docs/src/developing/deployed-programs/debugging.md
Normal file
118
docs/src/developing/deployed-programs/debugging.md
Normal 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`
|
20
docs/src/developing/deployed-programs/deploying.md
Normal file
20
docs/src/developing/deployed-programs/deploying.md
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
title: "Deploying"
|
||||
---
|
||||
|
||||

|
||||
|
||||
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.
|
213
docs/src/developing/deployed-programs/developing-c.md
Normal file
213
docs/src/developing/deployed-programs/developing-c.md
Normal 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
|
315
docs/src/developing/deployed-programs/developing-rust.md
Normal file
315
docs/src/developing/deployed-programs/developing-rust.md
Normal 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
|
56
docs/src/developing/deployed-programs/examples.md
Normal file
56
docs/src/developing/deployed-programs/examples.md
Normal 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).
|
81
docs/src/developing/deployed-programs/faq.md
Normal file
81
docs/src/developing/deployed-programs/faq.md
Normal 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)
|
178
docs/src/developing/deployed-programs/overview.md
Normal file
178
docs/src/developing/deployed-programs/overview.md
Normal 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)
|
210
docs/src/developing/programming-model/accounts.md
Normal file
210
docs/src/developing/programming-model/accounts.md
Normal 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)
|
||||
```
|
61
docs/src/developing/programming-model/compute-budget.md
Normal file
61
docs/src/developing/programming-model/compute-budget.md
Normal 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.
|
150
docs/src/developing/programming-model/cpi.md
Normal file
150
docs/src/developing/programming-model/cpi.md
Normal 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.
|
15
docs/src/developing/programming-model/overview.md
Normal file
15
docs/src/developing/programming-model/overview.md
Normal 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).
|
@@ -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.
|
26
docs/src/developing/programming-model/runtime-features.md
Normal file
26
docs/src/developing/programming-model/runtime-features.md
Normal 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).
|
92
docs/src/developing/programming-model/secpk1-instructions.md
Normal file
92
docs/src/developing/programming-model/secpk1-instructions.md
Normal 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.
|
121
docs/src/developing/programming-model/sysvars.md
Normal file
121
docs/src/developing/programming-model/sysvars.md
Normal 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)
|
111
docs/src/developing/programming-model/transactions.md
Normal file
111
docs/src/developing/programming-model/transactions.md
Normal 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.
|
Reference in New Issue
Block a user