[docs] updating the "writing programs" section (#29197)

* docs: added limitations page

* fix: updated deprecated cargo test-bpf

* docs: moved content off of overview

* fix: added compute budget description

* fix: updated compute buddget

* fix: rearranged sections

* fix: update code snippet

* docs: overview page and links
This commit is contained in:
Nick Frostbutter 2022-12-21 23:27:10 -05:00 committed by GitHub
parent 1e0d3931fd
commit 5918d6f09d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 276 additions and 219 deletions

View File

@ -240,6 +240,11 @@ module.exports = {
id: "developing/on-chain-programs/examples",
label: "Program Examples",
},
{
type: "doc",
id: "developing/on-chain-programs/limitations",
label: "Limitations",
},
{
type: "doc",
id: "developing/on-chain-programs/faq",

View File

@ -56,10 +56,9 @@ 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 SBF
loader](overview.md#versions) and the entrypoints may vary between them.
calls when invoking a program. Solana supports multiple versions of the SBF loader 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).
see the [FAQ section on Loaders](./faq.md#loaders).
Currently there are two supported loaders [SBF
Loader](https://github.com/solana-labs/solana/blob/7ddf10e602d2ed87a9e3737aa8c32f1db9f909d8/sdk/program/src/bpf_loader.rs#L17)
@ -104,7 +103,7 @@ 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.
[Input Parameter Serialization](./faq.md#input-parameter-serialization) docs.
## Data Types

View File

@ -52,7 +52,7 @@ For example:
on Rand](#depending-on-rand).
- Crates may overflow the stack even if the stack overflowing code isn't
included in the program itself. For more information refer to
[Stack](overview.md#stack).
[Stack](./faq.md#stack).
## How to Build
@ -95,10 +95,10 @@ program.
## 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.
calls when invoking a program. Solana supports multiple versions of the BPF
loader 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).
see the [FAQ section on Loaders](./faq.md#loaders).
Currently there are two supported loaders [BPF
Loader](https://github.com/solana-labs/solana/blob/d9b0fc0e3eec67dfe4a97d9298b15969b2804fab/sdk/program/src/bpf_loader.rs#L17)
@ -159,7 +159,7 @@ 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.
[Input Parameter Serialization](./faq.md#input-parameter-serialization) docs.
### Data Types
@ -211,7 +211,7 @@ 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:
single-threaded environment, as well as being deterministic:
- No access to
- `rand`

View File

@ -8,32 +8,36 @@ questions.
If not addressed here, ask on [StackOverflow](https://stackoverflow.com/questions/tagged/solana) with the `solana` tag or check out the Solana [#developer-support](https://discord.gg/RxeGBH)
## `CallDepth` error
## Limitations
This error means that that cross-program invocation exceeded the allowed
invocation call depth.
Developing programs on the Solana blockchain have some inherent limitation associated with them. Below is a list of common limitation that you may run into.
See [cross-program invocation Call
Depth](developing/programming-model/calling-between-programs.md#call-depth)
See [Limitations of developing programs](./limitations.md) for more details
## `CallDepthExceeded` error
## Berkeley Packet Filter (BPF)
This error means the SBF stack depth was exceeded.
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 [Berkeley Packet Filter (BPF)](https://en.wikipedia.org/wiki/Berkeley_Packet_Filter) bytecode.
See [call depth](overview.md#call-depth)
Because Solana uses the LLVM compiler infrastructure, a program may be written in any programming language that can target the LLVM's BPF backend.
## Computational constraints
BPF provides an efficient [instruction set](https://github.com/iovisor/bpf-docs/blob/master/eBPF.md) that can be executed in an interpreted virtual machine or as efficient just-in-time compiled native instructions.
See [computational
constraints](developing/programming-model/runtime.md#compute-budget)
## Memory map
## Float Rust types
The virtual address memory map used by Solana SBF programs is fixed and laid out
as follows
See [float support](overview.md#float-support)
- Program code starts at 0x100000000
- Stack data starts at 0x200000000
- Heap data starts at 0x300000000
- Program input parameters start at 0x400000000
## Heap size
See [heap](overview.md#heap)
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.
## InvalidAccountData
@ -75,6 +79,137 @@ See [Rust Project Dependencies](developing-rust.md#project-dependencies)
See [Rust restrictions](developing-rust.md#restrictions)
## Stack size
## Stack
See [stack](overview.md#stack)
SBF 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.
For example:
```
Error: Function _ZN16curve25519_dalek7edwards21EdwardsBasepointTable6create17h178b3d2411f7f082E Stack offset of -30728 exceeded max offset of -4096 by 26632 bytes, please minimize large stack variables
```
The message identifies which symbol is exceeding its stack frame, but the name
might be mangled if it is a Rust or C++ symbol.
> To demangle a Rust symbol use [rustfilt](https://github.com/luser/rustfilt).
The above warning came from a Rust program, so the demangled symbol name is:
```bash
rustfilt _ZN16curve25519_dalek7edwards21EdwardsBasepointTable6create17h178b3d2411f7f082E
curve25519_dalek::edwards::EdwardsBasepointTable::create
```
To demangle a C++ symbol use `c++filt` from binutils.
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.
SBF stack frames occupy a virtual address range starting at `0x200000000`.
## Heap size
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 program's
specific needs.
- [Rust program heap usage](developing-rust.md#heap)
- [C program heap usage](developing-c.md#heap)
## 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
SBF 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
SBF 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 SBF 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 SBF 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
SBF 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.
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)
The latest loader serializes the program input parameters as follows (all
encoding is little endian):
- 8 bytes unsigned number of accounts
- For each account
- 1 byte indicating if this is a duplicate account, if not a duplicate then
the value is 0xff, otherwise the value is the index of the account it is a
duplicate of.
- If duplicate: 7 bytes of padding
- If not duplicate:
- 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 bytes 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

View File

@ -0,0 +1,72 @@
---
title: "Limitations"
---
Developing programs on the Solana blockchain have some inherent limitation associated with them. Below is a list of common limitation that you may run into.
## Rust libraries
Since Rust based on-chain programs must run be deterministic while running in a resource-constrained, single-threaded environment, they have some limitations on various libraries.
See [Developing with Rust - Restrictions](./developing-rust.md#restrictions) for a detailed breakdown these restrictions and limitations.
## Compute budget
To prevent abuse of the blockchain's computational resources, each transaction is allocated a [compute budget](./../../terminology.md#compute-budget). Exceeding this compute budget will result in the transaction failing.
See [computational constraints](../programming-model/runtime.md#compute-budget) in the Runtime for more specific details.
## Call stack depth - `CallDepthExceeded` error
Solana programs are constrained to run quickly, and to facilitate this, the program's call stack is limited to a max depth of **64 frames**.
When a program exceeds the allowed call stack depth limit, it will receive the `CallDepthExceeded` error.
## CPI call depth - `CallDepth` error
Cross-program invocations allow programs to invoke other programs directly, but the depth is constrained currently to `4`.
When a program exceeds the allowed [cross-program invocation call depth](../programming-model/calling-between-programs.md#call-depth), it will receive a `CallDepth` error
## Float Rust types support
Programs support a limited subset of Rust's float operations. If a program
attempts to use a float operation that is not supported, the runtime will report
an unresolved symbol error.
Float operations are performed via software libraries, specifically LLVM's float
built-ins. Due to the software emulated, they consume more compute units than
integer operations. In general, fixed point operations are recommended where
possible.
The Solana Program Library math tests will report the performance of some math
operations: https://github.com/solana-labs/solana-program-library/tree/master/libraries/math
To run the test: sync the repo and run:
```sh
cargo test-sbf -- --nocapture --test-threads=1
```
Recent results show the float operations take more instructions compared to
integers equivalents. Fixed point implementations may vary but will also be
less than the float equivalents:
```
u64 f32
Multiply 8 176
Divide 9 219
```
## 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 SBF 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.

View File

@ -1,218 +1,64 @@
---
title: "Overview"
title: "Overview of Writing Programs"
sidebar_label: "Overview"
---
Developers can write and deploy their own programs to the Solana blockchain.
Developers can write and deploy their own programs to the Solana blockchain. While developing these "on-chain" programs can seem cumbersome, the entire process can be broadly summarized into a few key steps.
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.
## Solana Development Lifecycle
## Berkeley Packet Filter (BPF)
1. Setup your development environment
2. Write your program
3. Compile the program
4. Generate the program's public address
5. Deploy the program
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 [Berkeley Packet Filter
(BPF)](https://en.wikipedia.org/wiki/Berkeley_Packet_Filter) bytecode.
### 1. Setup your development environment
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++.
The most robust way of getting started with Solana development, is [installing the Solana CLI](./../../cli/install-solana-cli-tools.md) tools on your local computer. This will allow you to have the most powerful development environment.
BPF provides an efficient [instruction
set](https://github.com/iovisor/bpf-docs/blob/master/eBPF.md) that can be
executed in an interpreted virtual machine or as efficient just-in-time compiled
native instructions.
Some developers may also opt for using [Solana Playground](https://beta.solpg.io/), a browser based IDE. It will let you write, build, and deploy on-chain programs. All from your browser. No installation needed.
## Memory map
### 2. Write your program
The virtual address memory map used by Solana SBF programs is fixed and laid out
as follows
Writing Solana programs is most commonly done so using the Rust language. These Rust programs are effectively the same as creating a traditional [Rust library](https://doc.rust-lang.org/rust-by-example/crates/lib.html).
- Program code starts at 0x100000000
- Stack data starts at 0x200000000
- Heap data starts at 0x300000000
- Program input parameters start at 0x400000000
> You can read more about other [supported languages](#support-languages) below.
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.
### 3. Compile the program
## Stack
Once the program is written, it must be complied down to [Berkley Packet Filter](./faq.md#berkeley-packet-filter-bpf) byte-code that will then be deployed to the blockchain.
SBF uses stack frames instead of a variable stack pointer. Each stack frame is
4KB in size.
### 4. Generate the program's public address
If a program violates that stack frame size, the compiler will report the
overrun as a warning.
Using the [Solana CLI](./../../cli/install-solana-cli-tools.md), the developer will generate a new unique [Keypair](./../../terminology.md#keypair) for the new program. The public address (aka [Pubkey](./../../terminology.md#public-key-pubkey)) from this Keypair will be used on-chain as the program's public address (aka [`programId`](./../../terminology.md#program-id)).
For example: `Error: Function _ZN16curve25519_dalek7edwards21EdwardsBasepointTable6create17h178b3d2411f7f082E Stack offset of -30728 exceeded max offset of -4096 by 26632 bytes, please minimize large stack variables`
### 5. Deploying the program
The message identifies which symbol is exceeding its stack frame but the name
might be mangled if it is a Rust or C++ symbol. To demangle a Rust symbol use
[rustfilt](https://github.com/luser/rustfilt). The above warning came from a
Rust program, so the demangled symbol name is:
Then again using the CLI, the compiled program can be deployed to the selected blockchain cluster by creating many transactions containing the program's byte-code. Due to the transaction memory size limitations, each transaction effectively sends small chunks of the program to the blockchain in a rapid-fire manner.
```bash
$ rustfilt _ZN16curve25519_dalek7edwards21EdwardsBasepointTable6create17h178b3d2411f7f082E
curve25519_dalek::edwards::EdwardsBasepointTable::create
```
Once the entire program has been sent to the blockchain, a final transaction is sent to write all of the buffered byte-code to the program's data account. This either mark the new program as [`executable`](./../programming-model/accounts.md#executable), or complete the process to upgrade an existing program (if it already existed).
To demangle a C++ symbol use `c++filt` from binutils.
## Support languages
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.
Solana programs are typically written in the [Rust language](./developing-rust.md), but [C/C++](./developing-c.md) are also supported.
SBF stack frames occupy a virtual address range starting at 0x200000000.
There are also various community driven efforts to enable writing on-chain programs using other languages, including:
## Call Depth
- Python via [Seahorse](https://seahorse-lang.org/) (that acts as a wrapper the Rust based Anchor framework)
Programs are constrained to run quickly, and to facilitate this, the program's
call stack is limited to a max depth of 64 frames.
## Example programs
## Heap
The [Hello world example](examples.md#helloworld) is a good starting place to see how a program is written, built, deployed, and interacted with on-chain.
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.
You can also explore the [Program Examples](./examples.md) for other examples of on-chain programs.
Internally, programs have access to the 32KB memory region starting at virtual
address 0x300000000 and may implement a custom heap based on the program's
specific needs.
## Limitations
- [Rust program heap usage](developing-rust.md#heap)
- [C program heap usage](developing-c.md#heap)
As you dive deeper into program development, it is important to understand some of the important limitations associated with on-chain programs.
## Float Support
Read more details on the [Limitations](./limitations.md) page
Programs support a limited subset of Rust's float operations, if a program
attempts to use a float operation that is not supported, the runtime will report
an unresolved symbol error.
## Frequently asked questions
Float operations are performed via software libraries, specifically LLVM's float
builtins. Due to the software emulated they consume more compute units than
integer operations. In general, fixed point operations are recommended where
possible.
The Solana Program Library math tests will report the performance of some math
operations:
https://github.com/solana-labs/solana-program-library/tree/master/libraries/math
To run the test, sync the repo, and run:
`$ cargo test-bpf -- --nocapture --test-threads=1`
Recent results show the float operations take more instructions compared to
integers equivalents. Fixed point implementations may vary but will also be
less than the float equivalents:
```
u64 f32
Multipy 8 176
Divide 9 219
```
## 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 SBF 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
SBF 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
SBF 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 SBF 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 SBF 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
SBF 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.
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)
The latest loader serializes the program input parameters as follows (all
encoding is little endian):
- 8 bytes unsigned number of accounts
- For each account
- 1 byte indicating if this is a duplicate account, if not a duplicate then
the value is 0xff, otherwise the value is the index of the account it is a
duplicate of.
- If duplicate: 7 bytes of padding
- If not duplicate:
- 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 bytes 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
Discover many of the [frequently asked questions](./faq.md) other developers have about writing/understanding Solana programs.

View File

@ -43,7 +43,7 @@ solana config set --url localhost
## Create a new Rust library with Cargo
Solana programs written in Rust are _libraries_ which are compiled to [BPF bytecode](../developing/on-chain-programs/overview#berkeley-packet-filter-bpf) and saved in the `.so` format.
Solana programs written in Rust are _libraries_ which are compiled to [BPF bytecode](../developing/on-chain-programs/faq.md#berkeley-packet-filter-bpf) and saved in the `.so` format.
Initialize a new Rust library named `hello_world` via the Cargo command line:

View File

@ -48,7 +48,7 @@ The [validator](#validator) that produces the genesis (first) [block](#block) of
## BPF loader
The Solana program that owns and loads [BPF](developing/on-chain-programs/overview#berkeley-packet-filter-bpf) smart contract programs, allowing the program to interface with the runtime.
The Solana program that owns and loads [BPF](developing/on-chain-programs/faq#berkeley-packet-filter-bpf) smart contract programs, allowing the program to interface with the runtime.
## client
@ -259,7 +259,7 @@ See also [rent exempt](#rent-exempt) below. Learn more about rent here: [What is
## rent exempt
Accounts that maintain more than 2 years with of rent payments in their account are considered "*rent exempt*" and will not incur the [collection of rent](../src/developing/intro/rent.md#collecting-rent).
Accounts that maintain more than 2 years with of rent payments in their account are considered "_rent exempt_" and will not incur the [collection of rent](../src/developing/intro/rent.md#collecting-rent).
## root