Flesh out development docs (#13318)
* flesh out development docs * nits
This commit is contained in:
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)
|
Reference in New Issue
Block a user