Update development docs (#13661)
This commit is contained in:
Binary file not shown.
4
.cache/fontconfig/CACHEDIR.TAG
Normal file
4
.cache/fontconfig/CACHEDIR.TAG
Normal file
@ -0,0 +1,4 @@
|
||||
Signature: 8a477f597d28d172789f06886806bc55
|
||||
# This file is a cache directory tag created by fontconfig.
|
||||
# For information about cache directory tags, see:
|
||||
# http://www.brynosaurus.com/cachedir/
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -7,6 +7,7 @@ module.exports = {
|
||||
favicon: "img/favicon.ico",
|
||||
organizationName: "solana-labs", // Usually your GitHub org/user name.
|
||||
projectName: "solana", // Usually your repo name.
|
||||
onBrokenLinks: 'throw',
|
||||
themeConfig: {
|
||||
navbar: {
|
||||
logo: {
|
||||
@ -14,14 +15,14 @@ module.exports = {
|
||||
src: "img/logo-horizontal.svg",
|
||||
srcDark: "img/logo-horizontal-dark.svg",
|
||||
},
|
||||
links: [
|
||||
items: [
|
||||
{
|
||||
href: "https://spl.solana.com",
|
||||
label: "Program Library »",
|
||||
position: "left",
|
||||
},
|
||||
{
|
||||
to: "apps",
|
||||
to: "developing/programming-model/overview",
|
||||
label: "Develop",
|
||||
position: "left",
|
||||
},
|
||||
|
4748
docs/package-lock.json
generated
4748
docs/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -5,6 +5,8 @@
|
||||
"scripts": {
|
||||
"start": "docusaurus start",
|
||||
"build": "docusaurus build",
|
||||
"clear": "docusaurus clear",
|
||||
"help": "docusaurus --help",
|
||||
"swizzle": "docusaurus swizzle",
|
||||
"deploy": "docusaurus deploy",
|
||||
"format": "prettier --check \"**/*.{js,jsx,json,md,scss}\"",
|
||||
@ -13,8 +15,8 @@
|
||||
"lint:fix": "npm run lint -- --fix"
|
||||
},
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "^2.0.0-alpha.58",
|
||||
"@docusaurus/preset-classic": "^2.0.0-alpha.58",
|
||||
"@docusaurus/core": "^2.0.0-alpha.65",
|
||||
"@docusaurus/preset-classic": "^2.0.0-alpha.65",
|
||||
"@docusaurus/theme-search-algolia": "^2.0.0-alpha.32",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"clsx": "^1.1.1",
|
||||
@ -22,7 +24,9 @@
|
||||
"eslint-plugin-react": "^7.20.0",
|
||||
"prettier": "^2.0.5",
|
||||
"react": "^16.8.4",
|
||||
"react-dom": "^16.8.4"
|
||||
"react-dom": "^16.8.4",
|
||||
"rehype-katex": "^4.0.0",
|
||||
"remark-math": "^4.0.0"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
|
@ -28,7 +28,24 @@ fi
|
||||
cat > "$CONFIG_FILE" <<EOF
|
||||
{
|
||||
"name": "$PROJECT_NAME",
|
||||
"scope": "solana-labs"
|
||||
"scope": "solana-labs",
|
||||
"redirects": [
|
||||
{ "source": "/apps", "destination": "/developing/programming-model/overview" },
|
||||
{ "source": "/apps/bakcwards-compatibility/", "destination": "/developing/backwards-compatibility" },
|
||||
{ "source": "/apps/break/", "destination": "/developing/deployed-programs/examples" },
|
||||
{ "source": "/apps/builtins/", "destination": "/developing/builtin-programs" },
|
||||
{ "source": "/apps/drones/", "destination": "/developing/deployed-programs/examples" },
|
||||
{ "source": "/apps/hello-world/", "destination": "/developing/deployed-programs/examples" },
|
||||
{ "source": "/apps/javascript-api/", "destination": "/developing/clients/javascript-api" },
|
||||
{ "source": "/apps/jasonrpc-api/", "destination": "/developing/clients/jsonrpc-api" },
|
||||
{ "source": "/apps/programming-faq/", "destination": "/developing/deployed-programs/faq" },
|
||||
{ "source": "/apps/rent/", "destination": "/developing/programming-model/accounts" },
|
||||
{ "source": "/apps/sysvars/", "destination": "/developing/programming-model/sysvars" },
|
||||
{ "source": "/apps/webwallet/", "destination": "/developing/deployed-programs/examples" },
|
||||
{ "source": "/implemented-proposals/cross-program-invocation", "destination": "/developing/programming-model/cpi" },
|
||||
{ "source": "/implemented-proposals/program-derived-addresses", "destination": "/developing/programming-model/program-derived-addresses" },
|
||||
{ "source": "/implemented-proposals/secp256k1_instruction", "destination": "/developing/programming-model/secpk1-instructions" }
|
||||
]
|
||||
}
|
||||
EOF
|
||||
|
||||
|
182
docs/sidebars.js
182
docs/sidebars.js
@ -4,7 +4,7 @@ module.exports = {
|
||||
"introduction",
|
||||
"terminology",
|
||||
"history",
|
||||
],
|
||||
],
|
||||
"Wallets": [
|
||||
"wallet-guide",
|
||||
"wallet-guide/apps",
|
||||
@ -59,18 +59,47 @@ module.exports = {
|
||||
"cli/usage",
|
||||
],
|
||||
"Developing": [
|
||||
"apps",
|
||||
"apps/programming-faq",
|
||||
"apps/rent",
|
||||
"apps/hello-world",
|
||||
"apps/break",
|
||||
"apps/webwallet",
|
||||
"apps/drones",
|
||||
"transaction",
|
||||
"apps/jsonrpc-api",
|
||||
"apps/javascript-api",
|
||||
"apps/builtins",
|
||||
"apps/sysvars",
|
||||
{
|
||||
type: "category",
|
||||
label: "Programming Model",
|
||||
items: [
|
||||
"developing/programming-model/overview",
|
||||
"developing/programming-model/transactions",
|
||||
"developing/programming-model/accounts",
|
||||
"developing/programming-model/runtime",
|
||||
"developing/programming-model/calling-between-programs",
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "Clients",
|
||||
items: [
|
||||
"developing/clients/jsonrpc-api",
|
||||
"developing/clients/javascript-api",
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "Builtins",
|
||||
items: [
|
||||
"developing/builtins/programs",
|
||||
"developing/builtins/sysvars",
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "Deployed Programs",
|
||||
items: [
|
||||
"developing/deployed-programs/overview",
|
||||
"developing/deployed-programs/developing-rust",
|
||||
"developing/deployed-programs/developing-c",
|
||||
"developing/deployed-programs/deploying",
|
||||
"developing/deployed-programs/debugging",
|
||||
"developing/deployed-programs/examples",
|
||||
"developing/deployed-programs/faq",
|
||||
],
|
||||
},
|
||||
"developing/backwards-compatibility",
|
||||
],
|
||||
"Integrating": ["integrations/exchange"],
|
||||
"Validating": [
|
||||
@ -87,27 +116,27 @@ module.exports = {
|
||||
items: [
|
||||
"tour-de-sol",
|
||||
{
|
||||
type: "category",
|
||||
label: "Registration",
|
||||
items: [
|
||||
"tour-de-sol/registration/how-to-register",
|
||||
"tour-de-sol/registration/terms-of-participation",
|
||||
"tour-de-sol/registration/rewards",
|
||||
"tour-de-sol/registration/confidentiality",
|
||||
"tour-de-sol/registration/validator-registration-and-rewards-faq",
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "Participation",
|
||||
items: [
|
||||
"tour-de-sol/participation/validator-technical-requirements",
|
||||
"tour-de-sol/participation/validator-public-key-registration",
|
||||
"tour-de-sol/participation/steps-to-create-a-validator",
|
||||
],
|
||||
},
|
||||
"tour-de-sol/useful-links",
|
||||
"tour-de-sol/submitting-bugs",
|
||||
type: "category",
|
||||
label: "Registration",
|
||||
items: [
|
||||
"tour-de-sol/registration/how-to-register",
|
||||
"tour-de-sol/registration/terms-of-participation",
|
||||
"tour-de-sol/registration/rewards",
|
||||
"tour-de-sol/registration/confidentiality",
|
||||
"tour-de-sol/registration/validator-registration-and-rewards-faq",
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "Participation",
|
||||
items: [
|
||||
"tour-de-sol/participation/validator-technical-requirements",
|
||||
"tour-de-sol/participation/validator-public-key-registration",
|
||||
"tour-de-sol/participation/steps-to-create-a-validator",
|
||||
],
|
||||
},
|
||||
"tour-de-sol/useful-links",
|
||||
"tour-de-sol/submitting-bugs",
|
||||
],
|
||||
},
|
||||
"running-validator/validator-troubleshoot",
|
||||
@ -117,19 +146,19 @@ module.exports = {
|
||||
"cluster/bench-tps",
|
||||
"cluster/performance-metrics"
|
||||
],
|
||||
"Architecture": [
|
||||
"Architecture": [
|
||||
{
|
||||
type: "category",
|
||||
label: "Cluster",
|
||||
items: [
|
||||
"cluster/overview",
|
||||
"cluster/synchronization",
|
||||
"cluster/leader-rotation",
|
||||
"cluster/fork-generation",
|
||||
"cluster/managing-forks",
|
||||
"cluster/turbine-block-propagation",
|
||||
"cluster/vote-signing",
|
||||
"cluster/stake-delegation-and-rewards",
|
||||
"cluster/synchronization",
|
||||
"cluster/leader-rotation",
|
||||
"cluster/fork-generation",
|
||||
"cluster/managing-forks",
|
||||
"cluster/turbine-block-propagation",
|
||||
"cluster/vote-signing",
|
||||
"cluster/stake-delegation-and-rewards",
|
||||
],
|
||||
},
|
||||
{
|
||||
@ -144,80 +173,77 @@ module.exports = {
|
||||
"validator/runtime",
|
||||
],
|
||||
},
|
||||
],
|
||||
],
|
||||
"Design Proposals": [
|
||||
{
|
||||
type: "category",
|
||||
label: "Implemented",
|
||||
items: [
|
||||
"implemented-proposals/implemented-proposals",
|
||||
{
|
||||
type: "category",
|
||||
label: "Economic Design",
|
||||
items: [
|
||||
"implemented-proposals/ed_overview/ed_overview",
|
||||
{
|
||||
type: "category",
|
||||
label: "Validation Client Economics",
|
||||
label: "Economic Design",
|
||||
items: [
|
||||
"implemented-proposals/ed_overview/ed_validation_client_economics/ed_vce_overview",
|
||||
"implemented-proposals/ed_overview/ed_validation_client_economics/ed_vce_state_validation_protocol_based_rewards",
|
||||
"implemented-proposals/ed_overview/ed_validation_client_economics/ed_vce_state_validation_transaction_fees",
|
||||
"implemented-proposals/ed_overview/ed_validation_client_economics/ed_vce_validation_stake_delegation",
|
||||
"implemented-proposals/ed_overview/ed_overview",
|
||||
{
|
||||
type: "category",
|
||||
label: "Validation Client Economics",
|
||||
items: [
|
||||
"implemented-proposals/ed_overview/ed_validation_client_economics/ed_vce_overview",
|
||||
"implemented-proposals/ed_overview/ed_validation_client_economics/ed_vce_state_validation_protocol_based_rewards",
|
||||
"implemented-proposals/ed_overview/ed_validation_client_economics/ed_vce_state_validation_transaction_fees",
|
||||
"implemented-proposals/ed_overview/ed_validation_client_economics/ed_vce_validation_stake_delegation",
|
||||
],
|
||||
},
|
||||
"implemented-proposals/ed_overview/ed_storage_rent_economics",
|
||||
"implemented-proposals/ed_overview/ed_economic_sustainability",
|
||||
"implemented-proposals/ed_overview/ed_mvp",
|
||||
"implemented-proposals/ed_overview/ed_references",
|
||||
],
|
||||
},
|
||||
"implemented-proposals/ed_overview/ed_storage_rent_economics",
|
||||
"implemented-proposals/ed_overview/ed_economic_sustainability",
|
||||
"implemented-proposals/ed_overview/ed_mvp",
|
||||
"implemented-proposals/ed_overview/ed_references",
|
||||
],
|
||||
},
|
||||
"implemented-proposals/abi-management",
|
||||
"implemented-proposals/bank-timestamp-correction",
|
||||
"implemented-proposals/commitment",
|
||||
"implemented-proposals/cross-program-invocation",
|
||||
"implemented-proposals/durable-tx-nonces",
|
||||
"implemented-proposals/installer",
|
||||
"implemented-proposals/instruction_introspection",
|
||||
"implemented-proposals/leader-leader-transition",
|
||||
"implemented-proposals/leader-validator-transition",
|
||||
"implemented-proposals/persistent-account-storage",
|
||||
"implemented-proposals/program-derived-addresses",
|
||||
"implemented-proposals/readonly-accounts",
|
||||
"implemented-proposals/reliable-vote-transmission",
|
||||
"implemented-proposals/rent",
|
||||
"implemented-proposals/repair-service",
|
||||
"implemented-proposals/rpc-transaction-history",
|
||||
"implemented-proposals/secp256k1_instruction",
|
||||
"implemented-proposals/snapshot-verification",
|
||||
"implemented-proposals/staking-rewards",
|
||||
"implemented-proposals/testing-programs",
|
||||
"implemented-proposals/tower-bft",
|
||||
"implemented-proposals/transaction-fees",
|
||||
"implemented-proposals/validator-timestamp-oracle",
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "Accepted",
|
||||
items: [
|
||||
"proposals/accepted-design-proposals",
|
||||
"proposals/ledger-replication-to-implement",
|
||||
"proposals/optimistic-confirmation-and-slashing",
|
||||
"proposals/vote-signing-to-implement",
|
||||
"proposals/cluster-test-framework",
|
||||
"proposals/validator-proposal",
|
||||
"proposals/simple-payment-and-state-verification",
|
||||
"proposals/interchain-transaction-verification",
|
||||
"proposals/snapshot-verification",
|
||||
"proposals/bankless-leader",
|
||||
"proposals/slashing",
|
||||
"proposals/tick-verification",
|
||||
"proposals/block-confirmation",
|
||||
"proposals/rust-clients",
|
||||
"proposals/optimistic_confirmation",
|
||||
"proposals/embedding-move",
|
||||
"proposals/rip-curl",
|
||||
"proposals/ledger-replication-to-implement",
|
||||
"proposals/optimistic-confirmation-and-slashing",
|
||||
"proposals/vote-signing-to-implement",
|
||||
"proposals/cluster-test-framework",
|
||||
"proposals/validator-proposal",
|
||||
"proposals/simple-payment-and-state-verification",
|
||||
"proposals/interchain-transaction-verification",
|
||||
"proposals/snapshot-verification",
|
||||
"proposals/bankless-leader",
|
||||
"proposals/slashing",
|
||||
"proposals/tick-verification",
|
||||
"proposals/block-confirmation",
|
||||
"proposals/rust-clients",
|
||||
"proposals/optimistic_confirmation",
|
||||
"proposals/embedding-move",
|
||||
"proposals/rip-curl",
|
||||
]
|
||||
},
|
||||
],
|
||||
|
@ -1,53 +0,0 @@
|
||||
---
|
||||
title: Programming Model
|
||||
---
|
||||
|
||||
An _app_ interacts with a Solana cluster by sending it _transactions_ with one or more _instructions_. The Solana _runtime_ passes those instructions to _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.
|
||||
|
||||
### Accounts and 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.
|
||||
|
||||
The transaction also marks some accounts as _read-only accounts_. The runtime permits read-only accounts to be read concurrently. If a program attempts to modify a read-only account, the transaction is rejected by the runtime.
|
||||
|
||||
### Recent Blockhash
|
||||
|
||||
A transaction includes a recent 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.
|
||||
|
||||
### Instructions
|
||||
|
||||
Each instruction specifies a single program account \(which must be marked executable\), a subset of the transaction's accounts that should be passed to the program, and a data byte array instruction 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.
|
||||
|
||||
## Deploying Programs to a Cluster
|
||||
|
||||

|
||||
|
||||
As shown in the diagram above, a program author creates a program and compiles it to an ELF shared object containing BPF bytecode and uploads it to the Solana cluster with a special _deploy_ transaction. The cluster makes it available to clients via a _program ID_. The program ID is an _address_ specified when deploying and is used to reference the program in subsequent transactions.
|
||||
|
||||
A program may be written in any programming language that can target the Berkley Packet Filter \(BPF\) safe execution environment. The Solana SDK offers the best support for C/C++ and Rust programs, which are compiled to BPF using the [LLVM compiler infrastructure](https://llvm.org).
|
||||
|
||||
## Storing State between Transactions
|
||||
|
||||
If the program needs to store state between transactions, it does so using _accounts_. Accounts are similar to files in operating systems such as Linux. Like a file, an account may hold arbitrary data and that data persists beyond the lifetime of a program. Also like a file, an account includes metadata that tells the runtime who is allowed to access the data and how.
|
||||
|
||||
Unlike a file, the account includes metadata for the lifetime of the file. That lifetime is expressed in "tokens", which is a number of fractional native tokens, called _lamports_. Accounts are held in validator memory and pay ["rent"](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. To create an account with a public-key address, the client generates a _keypair_ and registers its public key using the `CreateAccount` instruction with preallocated fixed storage size in bytes. In fact, the account address can be an arbitrary 32 bytes, and there is a mechanism for advanced users to create derived addresses (`CreateAccountWithSeed`). Addresses are presented in Base58 encoding on user interfaces.
|
||||
|
||||
## Ownership of Accounts and Assignment to Programs
|
||||
|
||||
The created account is initialized to be _owned_ by a built-in program called the System program and is called a _system account_ aptly. An account includes "owner" metadata. The owner is a program ID. The runtime grants the program write access to the account if its ID matches the owner. For the case of the System program, the runtime allows clients to transfer lamports and importantly _assign_ account ownership, meaning changing owner to different program ID. If an account is not owned by a program, the program is only permitted to read its data and credit the account.
|
||||
|
||||
Also, if an account is marked "executable" in metadata, it will only be used by a _loader_ to run programs. For example, a BPF-compiled program is marked executable and loaded by the BPF loader when executing its transactions. No program is allowed to modify the contents of an executable account once deployed.
|
||||
|
||||
## Runtime Capability of Programs on Accounts
|
||||
|
||||
The runtime only permits the owner program to debit the account or modify its data. The program then defines additional rules for whether the client can modify accounts it owns. In the case of the System program, it allows users to transfer lamports by recognizing transaction signatures. If it sees the client signed the transaction using the keypair's _private key_, it knows the client authorized the token transfer.
|
||||
|
||||
In other words, the entire set of 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 none of the access rules were violated. If a program violates an access rule, the runtime discards all account changes made by all instructions and marks the transaction as failed.
|
||||
|
||||
## Smart Contracts
|
||||
|
||||
Programs don't always require transaction signatures, as the System program does. Instead, the program may manage _smart contracts_. A smart contract is a set of constraints that once satisfied, signal to a program that a token transfer or account update is permitted. For example, one could use the Budget program to create a smart contract that authorizes a token transfer only after some date. Once evidence that the date has past, the contract progresses, and token transfer completes.
|
@ -1,31 +0,0 @@
|
||||
---
|
||||
title: "Example: 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).
|
@ -1,63 +0,0 @@
|
||||
---
|
||||
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.
|
||||
|
||||
- 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.
|
@ -1,45 +0,0 @@
|
||||
---
|
||||
title: Drones
|
||||
---
|
||||
|
||||
This section defines an off-chain service called a _drone_, which acts as custodian of a user's private key. In its simplest form, it can be used to create _airdrop_ transactions, a token transfer from the drone's account to a client's account.
|
||||
|
||||
## Signing Service
|
||||
|
||||
A drone is a simple signing service. It listens for requests to sign _transaction data_. Once received, the drone validates the request however it sees fit. It may, for example, only accept transaction data with a `SystemInstruction::Transfer` instruction transferring only up to a certain amount of tokens. If the drone accepts the transaction, it returns an `Ok(Signature)` where `Signature` is a signature of the transaction data using the drone's private key. If it rejects the transaction data, it returns a `DroneError` describing why.
|
||||
|
||||
## Examples
|
||||
|
||||
### Granting access to an on-chain game
|
||||
|
||||
Creator of on-chain game tic-tac-toe hosts a drone that responds to airdrop requests containing an `InitGame` instruction. The drone signs the transaction data in the request and returns it, thereby authorizing its account to pay the transaction fee and as well as seeding the game's account with enough tokens to play it. The user then creates a transaction for its transaction data and the drones signature and submits it to the Solana cluster. Each time the user interacts with the game, the game pays the user enough tokens to pay the next transaction fee to advance the game. At that point, the user may choose to keep the tokens instead of advancing the game. If the creator wants to defend against that case, they could require the user to return to the drone to sign each instruction.
|
||||
|
||||
### Worldwide airdrop of a new token
|
||||
|
||||
Creator of a new on-chain token \(ERC-20 interface\), may wish to do a worldwide airdrop to distribute its tokens to millions of users over just a few seconds. That drone cannot spend resources interacting with the Solana cluster. Instead, the drone should only verify the client is unique and human, and then return the signature. It may also want to listen to the Solana cluster for recent entry IDs to support client retries and to ensure the airdrop is targeting the desired cluster.
|
||||
|
||||
Note: the Solana cluster will not parallelize transactions funded by the same fee-paying account. This means that the max throughput of a single fee-paying account is limited to the number of _ticks_ processed per second by the current leader. Add additional fee-paying accounts to improve throughput.
|
||||
|
||||
## Attack vectors
|
||||
|
||||
### Invalid recent_blockhash
|
||||
|
||||
The drone may prefer its airdrops only target a particular Solana cluster. To do that, it listens to the cluster for new entry IDs and ensure any requests reference a recent one.
|
||||
|
||||
Note: to listen for new entry IDs assumes the drone is either a validator or a _light_ client. At the time of this writing, light clients have not been implemented and no proposal describes them. This document assumes one of the following approaches be taken:
|
||||
|
||||
1. Define and implement a light client
|
||||
2. Embed a validator
|
||||
3. Query the jsonrpc API for the latest last id at a rate slightly faster than
|
||||
|
||||
ticks are produced.
|
||||
|
||||
### Double spends
|
||||
|
||||
A client may request multiple airdrops before the first has been submitted to the ledger. The client may do this maliciously or simply because it thinks the first request was dropped. The drone should not simply query the cluster to ensure the client has not already received an airdrop. Instead, it should use `recent_blockhash` to ensure the previous request is expired before signing another. Note that the Solana cluster will reject any transaction with a `recent_blockhash` beyond a certain _age_.
|
||||
|
||||
### Denial of Service
|
||||
|
||||
If the transaction data size is smaller than the size of the returned signature \(or descriptive error\), a single client can flood the network. Considering that a simple `Transfer` operation requires two public keys \(each 32 bytes\) and a `fee` field, and that the returned signature is 64 bytes \(and a byte to indicate `Ok`\), consideration for this attack may not be required.
|
||||
|
||||
In the current design, the drone accepts TCP connections. This allows clients to DoS the service by simply opening lots of idle connections. Switching to UDP may be preferred. The transaction data will be smaller than a UDP packet since the transaction sent to the Solana cluster is already pinned to using UDP.
|
@ -1,23 +0,0 @@
|
||||
---
|
||||
title: "Example: Hello World"
|
||||
---
|
||||
|
||||
Hello World is a project that demonstrates how to use the Solana Javascript API
|
||||
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).
|
@ -1,136 +0,0 @@
|
||||
---
|
||||
title: "Programming 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
|
||||
|
||||
Cross-program invocations allow programs to invoke other programs directly but
|
||||
the depth is constrained currently to 4.
|
||||
|
||||
## CallDepthExceeded
|
||||
|
||||
Programs are constrained to run quickly, and to facilitate this, the program's
|
||||
call stack is limited to max depth. If this error is encountered, then the
|
||||
program itself or its dependent crate packages have exceeded the max stack
|
||||
depth.
|
||||
|
||||
## Computational constraints
|
||||
|
||||
To prevent a program from abusing computation resources, a cap is enforced
|
||||
during execution. The following operations incur a cost:
|
||||
- Executing BPF instructions
|
||||
- Calling system calls (logging, creating program addresses, ...)
|
||||
- Cross-program invocations incur a base cost and the cost of the program
|
||||
invoked.
|
||||
|
||||
## Float Rust types
|
||||
|
||||
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. Be sure to include integration tests against a local
|
||||
cluster to ensure the operation is supported.
|
||||
|
||||
## Heap size
|
||||
|
||||
Programs have access to a 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.
|
||||
|
||||
## 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/master/sdk/src/program_pack.rs
|
||||
|
||||
|
||||
## MissingRequiredSignature
|
||||
|
||||
Some instructions require the account to be a signer; this error is returned if
|
||||
an account expected to be signed 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` don't match the signer seeds used to
|
||||
create the program address (`create_program_address`).
|
||||
|
||||
## `rand` dependency causes compilation failure
|
||||
|
||||
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"] }
|
||||
```
|
||||
|
||||
## Rust restrictions
|
||||
|
||||
There are some Rust limitations since programs run in a resource-constrained,
|
||||
single-threaded environment, and must be deterministic:
|
||||
|
||||
- No access to
|
||||
- std::fs
|
||||
- std::net
|
||||
- std::os
|
||||
- std::future
|
||||
- std::net
|
||||
- std::process
|
||||
- std::sync
|
||||
- std::task
|
||||
- std::thread
|
||||
- std::time
|
||||
- Limited access to:
|
||||
- std::os
|
||||
- rand or any crates that depend on it
|
||||
- Bincode is extremely computationally expensive in both cycles and call depth and should be avoided
|
||||
- String formatting should be avoided since it is also computational expensive
|
||||
- No support for `println!`, `print!`, the Solana SDK helpers in `src/log.rs`
|
||||
should be used instead
|
||||
|
||||
## Stack size
|
||||
|
||||
Solana programs compile down to Berkley Packet Filter instructions, which use
|
||||
stack frames instead of a variable stack pointer. Each stack frame is limited
|
||||
to 4KB. 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.
|
@ -1,57 +0,0 @@
|
||||
---
|
||||
title: Storage Rent for Accounts
|
||||
---
|
||||
|
||||
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)
|
||||
```
|
@ -1,17 +0,0 @@
|
||||
---
|
||||
title: "Example Client: Web Wallet"
|
||||
---
|
||||
|
||||
## Build and run a web wallet locally
|
||||
|
||||
First fetch the example code:
|
||||
|
||||
```bash
|
||||
$ git clone https://github.com/solana-labs/example-webwallet.git
|
||||
$ cd example-webwallet
|
||||
$ TAG=$(git describe --tags $(git rev-list --tags
|
||||
--max-count=1))
|
||||
$ git checkout $TAG
|
||||
```
|
||||
|
||||
Next, follow the steps in the git repository's [README](https://github.com/solana-labs/example-webwallet/blob/master/README.md).
|
@ -59,7 +59,7 @@ pubkey: GKvqsuNcnwWqPzzuhLmGi4rzzh55FhJtGizkhHaEJqiV
|
||||
```
|
||||
|
||||
You can also create a second (or more) wallet of any type:
|
||||
[paper](../paper-wallet/paper-wallet-usage.md#creating-multiple-paper-wallet-addresses),
|
||||
[paper](../wallet-guide/paper-wallet#creating-multiple-paper-wallet-addresses),
|
||||
[file system](../wallet-guide/file-system-wallet.md#creating-multiple-file-system-wallet-addresses),
|
||||
or [hardware](../wallet-guide/hardware-wallets.md#multiple-addresses-on-a-single-hardware-wallet).
|
||||
|
||||
|
@ -117,7 +117,7 @@ Currently, rewards and inflation are disabled.
|
||||
- If you have paid money to purchase/be issued tokens, such as through our
|
||||
CoinList auction, these tokens will be transferred on Mainnet Beta.
|
||||
- Note: If you are using a non-command-line wallet such as
|
||||
[Trust Wallet](wallet-guide/trust-wallet.md),
|
||||
[Solflare](wallet-guide/solflare.md),
|
||||
the wallet will always be connecting to Mainnet Beta.
|
||||
- Gossip entrypoint for Mainnet Beta: `mainnet-beta.solana.com:8001`
|
||||
- Metrics environment variable for Mainnet Beta:
|
||||
|
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.
|
118
docs/src/developing/builtins/programs.md
Normal file
118
docs/src/developing/builtins/programs.md
Normal file
@ -0,0 +1,118 @@
|
||||
---
|
||||
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.
|
||||
|
||||
For each builtin program the program id and description each supported
|
||||
instruction is provided. A transaction can mix and match instructions from different
|
||||
programs, as well include instructions from deployed 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.
|
||||
|
||||
## Secp256k1 Program
|
||||
|
||||
Verify secp256k1 public key recovery operations (ecrecover).
|
||||
|
||||
- Program id: `KeccakSecp256k11111111111111111111111111111`
|
||||
- Instructions: [new_secp256k1_instruction](https://github.com/solana-labs/solana/blob/c1f3f9d27b5f9534f9a37704bae1d690d4335b6b/programs/secp256k1/src/lib.rs#L18)
|
||||
|
||||
The secp256k1 program processes an 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.
|
@ -5,13 +5,14 @@ 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),
|
||||
[`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 is always
|
||||
*readonly*.
|
||||
instruction processor like any other account. Access to sysvars accounts ßis
|
||||
always *readonly*.
|
||||
|
||||
## Clock
|
||||
|
||||
@ -47,11 +48,12 @@ epoch, and estimated wall-clock Unix timestamp. It is updated every slot.
|
||||
|
||||
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))
|
||||
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)
|
||||
- Layout:
|
||||
[EpochSchedule](https://docs.rs/solana-program/VERSION_FOR_DOCS_RS/solana_program/epoch_schedule/struct.EpochSchedule.html)
|
||||
|
||||
## Fees
|
||||
|
||||
@ -59,7 +61,8 @@ 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)
|
||||
- Layout:
|
||||
[Fees](https://docs.rs/solana-program/VERSION_FOR_DOCS_RS/solana_program/sysvar/fees/struct.Fees.html)
|
||||
|
||||
## Instructions
|
||||
|
||||
@ -69,7 +72,8 @@ 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)
|
||||
- Layout:
|
||||
[Instructions](https://docs.rs/solana-program/VERSION_FOR_DOCS_RS/solana_program/sysvar/instructions/type.Instructions.html)
|
||||
|
||||
## RecentBlockhashes
|
||||
|
||||
@ -77,7 +81,8 @@ 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)
|
||||
- Layout:
|
||||
[RecentBlockhashes](https://docs.rs/solana-program/VERSION_FOR_DOCS_RS/solana_program/sysvar/recent_blockhashes/struct.RecentBlockhashes.html)
|
||||
|
||||
## Rent
|
||||
|
||||
@ -85,7 +90,8 @@ 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)
|
||||
- Layout:
|
||||
[Rent](https://docs.rs/solana-program/VERSION_FOR_DOCS_RS/solana_program/rent/struct.Rent.html)
|
||||
|
||||
## SlotHashes
|
||||
|
||||
@ -93,7 +99,8 @@ 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)
|
||||
- Layout:
|
||||
[SlotHashes](https://docs.rs/solana-program/VERSION_FOR_DOCS_RS/solana_program/slot_hashes/struct.SlotHashes.html)
|
||||
|
||||
## SlotHistory
|
||||
|
||||
@ -101,7 +108,8 @@ 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)
|
||||
- Layout:
|
||||
[SlotHistory](https://docs.rs/solana-program/VERSION_FOR_DOCS_RS/solana_program/slot_history/struct.SlotHistory.html)
|
||||
|
||||
## StakeHistory
|
||||
|
||||
@ -109,4 +117,5 @@ 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)
|
||||
- Layout:
|
||||
[StakeHistory](https://docs.rs/solana-program/VERSION_FOR_DOCS_RS/solana_program/stake_history/struct.StakeHistory.html)
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
title: JavaScript API
|
||||
title: Web3 JavaScript API
|
||||
---
|
||||
|
||||
See [solana-web3](https://solana-labs.github.io/solana-web3.js/).
|
@ -484,7 +484,7 @@ The result field will be an object with the following fields:
|
||||
- `transactions: <array>` - an array of JSON objects containing:
|
||||
- `transaction: <object|[string,encoding]>` - [Transaction](#transaction-structure) object, either in JSON format or encoded binary data, depending on encoding parameter
|
||||
- `meta: <object>` - transaction status metadata object, containing `null` or:
|
||||
- `err: <object | null>` - Error if transaction failed, null if transaction succeeded. [TransactionError definitions](https://github.com/solana-labs/solana/blob/master/sdk/src/transaction.rs#L14)
|
||||
- `err: <object | null>` - Error if transaction failed, null if transaction succeeded. [TransactionError definitions](https://github.com/solana-labs/solana/blob/master/sdk/src/transaction.rs#L24)
|
||||
- `fee: <u64>` - fee this transaction was charged, as u64 integer
|
||||
- `preBalances: <array>` - array of u64 account balances from before the transaction was processed
|
||||
- `postBalances: <array>` - array of u64 account balances after the transaction was processed
|
||||
@ -639,7 +639,7 @@ Result:
|
||||
|
||||
#### Transaction Structure
|
||||
|
||||
Transactions are quite different from those on other blockchains. Be sure to review [Anatomy of a Transaction](../transaction.md) to learn about transactions on Solana.
|
||||
Transactions are quite different from those on other blockchains. Be sure to review [Anatomy of a Transaction](developing/programming-model/transactions.md) to learn about transactions on Solana.
|
||||
|
||||
The JSON structure of a transaction is defined as follows:
|
||||
|
||||
@ -797,7 +797,7 @@ from newest to oldest transaction:
|
||||
* `<object>`
|
||||
* `signature: <string>` - transaction signature as base-58 encoded string
|
||||
* `slot: <u64>` - The slot that contains the block with the transaction
|
||||
* `err: <object | null>` - Error if transaction failed, null if transaction succeeded. [TransactionError definitions](https://github.com/solana-labs/solana/blob/master/sdk/src/transaction.rs#L14)
|
||||
* `err: <object | null>` - Error if transaction failed, null if transaction succeeded. [TransactionError definitions](https://github.com/solana-labs/solana/blob/master/sdk/src/transaction.rs#L24)
|
||||
* `memo: <string |null>` - Memo associated with the transaction, null if no memo is present
|
||||
|
||||
#### Example:
|
||||
@ -851,7 +851,7 @@ N encoding attempts to use program-specific instruction parsers to return more h
|
||||
- `slot: <u64>` - the slot this transaction was processed in
|
||||
- `transaction: <object|[string,encoding]>` - [Transaction](#transaction-structure) object, either in JSON format or encoded binary data, depending on encoding parameter
|
||||
- `meta: <object | null>` - transaction status metadata object:
|
||||
- `err: <object | null>` - Error if transaction failed, null if transaction succeeded. [TransactionError definitions](https://github.com/solana-labs/solana/blob/master/sdk/src/transaction.rs#L14)
|
||||
- `err: <object | null>` - Error if transaction failed, null if transaction succeeded. [TransactionError definitions](https://github.com/solana-labs/solana/blob/master/sdk/src/transaction.rs#L24)
|
||||
- `fee: <u64>` - fee this transaction was charged, as u64 integer
|
||||
- `preBalances: <array>` - array of u64 account balances from before the transaction was processed
|
||||
- `postBalances: <array>` - array of u64 account balances after the transaction was processed
|
||||
@ -1939,7 +1939,7 @@ An array of:
|
||||
- `<object>`
|
||||
- `slot: <u64>` - The slot the transaction was processed
|
||||
- `confirmations: <usize | null>` - Number of blocks since signature confirmation, null if rooted, as well as finalized by a supermajority of the cluster
|
||||
- `err: <object | null>` - Error if transaction failed, null if transaction succeeded. [TransactionError definitions](https://github.com/solana-labs/solana/blob/master/sdk/src/transaction.rs#L14)
|
||||
- `err: <object | null>` - Error if transaction failed, null if transaction succeeded. [TransactionError definitions](https://github.com/solana-labs/solana/blob/master/sdk/src/transaction.rs#L24)
|
||||
- DEPRECATED: `status: <object>` - Transaction status
|
||||
- `"Ok": <null>` - Transaction was successful
|
||||
- `"Err": <ERR>` - Transaction failed with TransactionError
|
||||
@ -2557,6 +2557,7 @@ The result field will be a JSON object with the following fields:
|
||||
|
||||
#### Example:
|
||||
|
||||
Request:
|
||||
```bash
|
||||
curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d '
|
||||
{"jsonrpc":"2.0","id":1, "method":"getVersion"}
|
||||
@ -2565,7 +2566,7 @@ curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d '
|
||||
|
||||
Result:
|
||||
```json
|
||||
{"jsonrpc":"2.0","result":{"solana-core": "1.4.10"},"id":1}
|
||||
{"jsonrpc":"2.0","result":{"solana-core": "1.5.0"},"id":1}
|
||||
```
|
||||
|
||||
### getVoteAccounts
|
||||
@ -2748,7 +2749,7 @@ Simulate sending a transaction
|
||||
An RpcResponse containing a TransactionStatus object
|
||||
The result will be an RpcResponse JSON object with `value` set to a JSON object with the following fields:
|
||||
|
||||
- `err: <object | string | null>` - Error if transaction failed, null if transaction succeeded. [TransactionError definitions](https://github.com/solana-labs/solana/blob/master/sdk/src/transaction.rs#L14)
|
||||
- `err: <object | string | null>` - Error if transaction failed, null if transaction succeeded. [TransactionError definitions](https://github.com/solana-labs/solana/blob/master/sdk/src/transaction.rs#L24)
|
||||
- `logs: <array | null>` - Array of log messages the transaction instructions output during execution, null if simulation failed before the transaction was able to execute (for example due to an invalid blockhash or signature verification failure)
|
||||
|
||||
#### Example:
|
111
docs/src/developing/deployed-programs/debugging.md
Normal file
111
docs/src/developing/deployed-programs/debugging.md
Normal file
@ -0,0 +1,111 @@
|
||||
---
|
||||
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
|
||||
|
||||
- [Testing with Rust](developing-rust.md#how-to-test)
|
||||
- [Testing with C](developing-c.md#how-to-test)
|
||||
|
||||
## 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/runtime.md/#compute-budget)
|
||||
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.
|
203
docs/src/developing/deployed-programs/developing-c.md
Normal file
203
docs/src/developing/deployed-programs/developing-c.md
Normal file
@ -0,0 +1,203 @@
|
||||
---
|
||||
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/runtime.md/#compute-budget)
|
||||
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
|
||||
|
||||
The [Solana Program Library github](https://github.com/solana-labs/solana-program-library/tree/master/examples/c) repo contains a collection of C examples
|
393
docs/src/developing/deployed-programs/developing-rust.md
Normal file
393
docs/src/developing/deployed-programs/developing-rust.md
Normal file
@ -0,0 +1,393 @@
|
||||
---
|
||||
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/calling-between-programs.md#cross-program-invocations).
|
||||
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.
|
||||
|
||||
Solana BPF programs have some [restrictions](#Restrictions) that may prevent the
|
||||
inclusion of some crates as dependencies or require special handling.
|
||||
|
||||
For example:
|
||||
- Crates that require the architecture be a subset of the ones supported by the
|
||||
official toolchain. There is no workaround for this unless that crate is
|
||||
forked and BPF added to that those architecture checks.
|
||||
- Crates may depend on `rand` which is not supported in Solana's deterministic
|
||||
program environment. To include a `rand` dependent crate refer to [Depending
|
||||
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).
|
||||
|
||||
## 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
|
||||
|
||||
Solana programs can be unit tested via the traditional `cargo test` mechanism by
|
||||
exercising program functions directly.
|
||||
|
||||
To help facilitate testing in an environment that more closely matches a live
|
||||
cluster, developers can use the
|
||||
[`program-test`](https://crates.io/crates/solana-program-test) crate. The
|
||||
`program-test` crate starts up a local instance of the runtime and allows tests
|
||||
to send multiple transactions while keeping state for the duration of the test.
|
||||
|
||||
For more information the [test in sysvar
|
||||
example](https://github.com/solana-labs/solana-program-library/blob/master/examples/rust/sysvar/tests/functional.rs)
|
||||
shows how an instruction containing syavar account is sent and processed by the
|
||||
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.
|
||||
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/runtime.md#compute-budget) for more
|
||||
information.
|
||||
|
||||
## Depending on Rand
|
||||
|
||||
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"] }
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
## Panicking
|
||||
|
||||
Rust's `panic!`, `assert!`, and internal panic results are printed to the
|
||||
[program logs](debugging.md#logging) by default.
|
||||
|
||||
```
|
||||
INFO solana_runtime::message_processor] Finalized account CGLhHSuWsp1gT4B7MY2KACqp9RUwQRhcUFfVSuxpSajZ
|
||||
INFO solana_runtime::message_processor] Call BPF program CGLhHSuWsp1gT4B7MY2KACqp9RUwQRhcUFfVSuxpSajZ
|
||||
INFO solana_runtime::message_processor] Program log: Panicked at: 'assertion failed: `(left == right)`
|
||||
left: `1`,
|
||||
right: `2`', rust/panic/src/lib.rs:22:5
|
||||
INFO solana_runtime::message_processor] BPF program consumed 5453 of 200000 units
|
||||
INFO solana_runtime::message_processor] BPF program CGLhHSuWsp1gT4B7MY2KACqp9RUwQRhcUFfVSuxpSajZ failed: BPF program panicked
|
||||
```
|
||||
|
||||
### Custom Panic Handler
|
||||
|
||||
Programs can override the default panic handler by providing their own
|
||||
implementation.
|
||||
|
||||
First define the `custom-panic` feature in the program's `Cargo.toml`
|
||||
|
||||
```toml
|
||||
[features]
|
||||
default = ["custom-panic"]
|
||||
custom-panic = []
|
||||
```
|
||||
|
||||
Then provide a custom implementation of the panic handler:
|
||||
|
||||
```rust
|
||||
#[cfg(all(feature = "custom-panic", target_arch = "bpf"))]
|
||||
#[no_mangle]
|
||||
fn custom_panic(info: &core::panic::PanicInfo<'_>) {
|
||||
solana_program::info!("program custom panic enabled");
|
||||
solana_program::info!(&format!("{}", info));
|
||||
}
|
||||
```
|
||||
|
||||
In the above snippit, the default implementation is shown, but developers may
|
||||
replace that with something that better suits their needs.
|
||||
|
||||
One of the side effects of supporting full panic messages by default is that
|
||||
programs incur the cost of pulling in more of Rust's `libstd` implementation
|
||||
into program's shared object. Typical programs will already be pulling in a
|
||||
fair amount of `libstd` and may not notice much of an increase in the shared
|
||||
object size. But programs that explicitly attempt to be very small by avoiding
|
||||
`libstd` may take a significant impact (~25kb). To eliminate that impact,
|
||||
programs can provide their own custom panic handler with an empty
|
||||
implementation.
|
||||
|
||||
```rust
|
||||
#[cfg(all(feature = "custom-panic", target_arch = "bpf"))]
|
||||
#[no_mangle]
|
||||
fn custom_panic(info: &core::panic::PanicInfo<'_>) {
|
||||
// Do nothing to save space
|
||||
}
|
||||
```
|
||||
|
||||
## 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/runtime.md#compute-budget)
|
||||
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
|
||||
|
||||
The [Solana Program Library
|
||||
github](https://github.com/solana-labs/solana-program-library/tree/master/examples/rust)
|
||||
repo contains a collection of Rust examples.
|
62
docs/src/developing/deployed-programs/examples.md
Normal file
62
docs/src/developing/deployed-programs/examples.md
Normal file
@ -0,0 +1,62 @@
|
||||
---
|
||||
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
|
||||
|
||||
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 play-though, 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
|
||||
|
||||
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).
|
||||
|
||||
## Language Specific
|
||||
|
||||
- [Rust](developing-rust.md#examples)
|
||||
- [C](developing-c.md#examples)
|
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/calling-between-programs.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/runtime.md#compute-budget)
|
||||
|
||||
## 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/calling-between-programs.md)
|
||||
don't match the signer seeds used to create the program address
|
||||
[`create_program_address`](developing/programming-model/calling-between-programs.md#program-derived-addresses).
|
||||
|
||||
## `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)
|
198
docs/src/developing/deployed-programs/overview.md
Normal file
198
docs/src/developing/deployed-programs/overview.md
Normal file
@ -0,0 +1,198 @@
|
||||
---
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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)
|
175
docs/src/developing/programming-model/accounts.md
Normal file
175
docs/src/developing/programming-model/accounts.md
Normal file
@ -0,0 +1,175 @@
|
||||
---
|
||||
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"](#rent) to stay there. Each validator periodically scans all accounts
|
||||
and collects rent. Any account that drops to zero lamports is purged. Accounts
|
||||
can also be marked [rent-exempt](#rent-exemption) if they contain a sufficnet
|
||||
number of lamports.
|
||||
|
||||
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 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 [indicate](transactions.md#message-header-format) that some of
|
||||
the accounts it references be treated as _read-only accounts_ in order to enable
|
||||
parallel account processing between transactions. 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 then it is considered a
|
||||
program which can be executed by including the account's public key an
|
||||
instruction's [program id](transactions.md#program-id). Accounts are marked as
|
||||
executable during a successful program deployment process by the loader that
|
||||
owns the account. For example, during BPF program deployment, once the loader
|
||||
has determined that the BPF bytecode in the account's data is valid, the loader
|
||||
permanently marks the program account as executable. Once executable, the
|
||||
runtime enforces that the account's data (the program) is immutable.
|
||||
|
||||
## 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`](calling-between-programs.md#program-derived-addresses)).
|
||||
|
||||
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.
|
||||
|
||||
## 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](developing/clients/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)
|
||||
```
|
@ -0,0 +1,281 @@
|
||||
---
|
||||
title: Calling Between Programs
|
||||
---
|
||||
|
||||
## Cross-Program Invocations
|
||||
|
||||
The Solana runtime allows programs to call each other via a mechanism called
|
||||
cross-program invocation. Calling between programs is achieved by one program
|
||||
invoking an instruction of the other. The invoking program is halted until the
|
||||
invoked program finishes processing the instruction.
|
||||
|
||||
For example, a client could create a transaction that modifies two accounts,
|
||||
each owned by separate on-chain programs:
|
||||
|
||||
```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);
|
||||
```
|
||||
|
||||
A client may to instead 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);
|
||||
```
|
||||
|
||||
Given two on-chain programs `token` and `acme`, each implementing instructions
|
||||
`pay()` and `launch_missiles()` respectively, acme can be implemented with a
|
||||
call to a function defined in the `token` module by issuing a cross-program
|
||||
invocation:
|
||||
|
||||
```rust,ignore
|
||||
mod acme {
|
||||
use token_instruction;
|
||||
|
||||
fn launch_missiles(accounts: &[AccountInfo]) -> Result<()> {
|
||||
...
|
||||
}
|
||||
|
||||
fn pay_and_launch_missiles(accounts: &[AccountInfo]) -> Result<()> {
|
||||
let alice_pubkey = accounts[1].key;
|
||||
let instruction = token_instruction::pay(&alice_pubkey);
|
||||
invoke(&instruction, accounts)?;
|
||||
|
||||
launch_missiles(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.
|
||||
|
||||
Note that `invoke` requires the caller to pass all the accounts required by the
|
||||
instruction being invoked. This means that both the executable account (the
|
||||
ones that matches the instruction's program id) and the accounts passed to the
|
||||
instruction procesor.
|
||||
|
||||
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).
|
||||
|
||||
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.
|
||||
|
||||
## Program Derived Addresses
|
||||
|
||||
Program derived addresses allow programmaticly generated signature to be used
|
||||
when [calling between programs](#cross-program-invocations).
|
||||
|
||||
Using a program derived address, a program may be given the authority over an
|
||||
account and later transfer that authority to another. This is possible because
|
||||
the program can 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.
|
||||
|
||||
Program derived address:
|
||||
|
||||
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-invocations).
|
||||
|
||||
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.
|
||||
|
||||
## Examples
|
||||
|
||||
Refer to [Developing with
|
||||
Rust](developing/deployed-programs/../../../deployed-programs/developing-rust.md#examples)
|
||||
and [Developing with
|
||||
C](developing/deployed-programs/../../../deployed-programs/developing-c.md#examples)
|
||||
for examples of how to use cross-program invocation.
|
17
docs/src/developing/programming-model/overview.md
Normal file
17
docs/src/developing/programming-model/overview.md
Normal file
@ -0,0 +1,17 @@
|
||||
---
|
||||
title: "Overview"
|
||||
---
|
||||
|
||||
An [app](terminology.md#app) interacts with a Solana cluster by sending it
|
||||
[transactions](transactions.md) with one or more
|
||||
[instructions](transactions.md#instructions). The Solana [runtime](runtime.md)
|
||||
passes those instructions to [programs](terminology.md#program) deployed by app developers
|
||||
beforehand. An instruction might, for example, tell a program to transfer
|
||||
[lamports](terminology.md#lamports) from one [account](accounts.md) 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).
|
127
docs/src/developing/programming-model/runtime.md
Normal file
127
docs/src/developing/programming-model/runtime.md
Normal file
@ -0,0 +1,127 @@
|
||||
---
|
||||
title: "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.
|
||||
|
||||
## 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.md#features) work and what features are enabled on the
|
||||
cluster being used are required to determine the current budget's values.
|
||||
|
||||
## New 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).
|
192
docs/src/developing/programming-model/transactions.md
Normal file
192
docs/src/developing/programming-model/transactions.md
Normal file
@ -0,0 +1,192 @@
|
||||
---
|
||||
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.
|
||||
|
||||
## Anatomy of a Transaction
|
||||
|
||||
This section covers the binary format of a transaction.
|
||||
|
||||
### Transaction Format
|
||||
|
||||
A transaction contains a [compact-array](#compact-array-format) of signatures,
|
||||
followed by a [message](#message-format). Each item in the signatures array is
|
||||
a [digital signature](#signature-format) of the given message. The Solana
|
||||
runtime verifies that the number of signatures matches the number in the first
|
||||
8 bits of the [message header](#message-header-format). It also verifies that
|
||||
each signature was signed by the private key corresponding to the public key at
|
||||
the same index in the message's account addresses array.
|
||||
|
||||
#### Signature Format
|
||||
|
||||
Each digital signature is in the ed25519 binary format and consumes 64 bytes.
|
||||
|
||||
### Message Format
|
||||
|
||||
A message contains a [header](#message-header-format), followed by a
|
||||
compact-array of [account addresses](#account-addresses-format), followed by a
|
||||
recent [blockhash](#blockhash-format), followed by a compact-array of
|
||||
[instructions](#instruction-format).
|
||||
|
||||
#### Message Header Format
|
||||
|
||||
The message header contains three unsigned 8-bit values. The first value is the
|
||||
number of required signatures in the containing transaction. The second value
|
||||
is the number of those corresponding account addresses that are read-only. The
|
||||
third value in the message header is the number of read-only account addresses
|
||||
not requiring signatures.
|
||||
|
||||
#### Account Addresses Format
|
||||
|
||||
The addresses that require signatures appear at the beginning of the account
|
||||
address array, with addresses requesting write access first and read-only
|
||||
accounts following. The addresses that do not require signatures follow the
|
||||
addresses that do, again with read-write accounts first and read-only accounts
|
||||
following.
|
||||
|
||||
#### Blockhash Format
|
||||
|
||||
A blockhash contains a 32-byte SHA-256 hash. It is used to indicate when a
|
||||
client last observed the ledger. Validators will reject transactions when the
|
||||
blockhash is too old.
|
||||
|
||||
### Instruction Format
|
||||
|
||||
An instruction contains a program id index, followed by a compact-array of
|
||||
account address indexes, followed by a compact-array of opaque 8-bit data. The
|
||||
program id index is used to identify an on-chain program that can interpret the
|
||||
opaque data. The program id index is an unsigned 8-bit index to an account
|
||||
address in the message's array of account addresses. The account address
|
||||
indexes are each an unsigned 8-bit index into that same array.
|
||||
|
||||
### Compact-Array Format
|
||||
|
||||
A compact-array is serialized as the array length, followed by each array item.
|
||||
The array length is a special multi-byte encoding called compact-u16.
|
||||
|
||||
#### Compact-u16 Format
|
||||
|
||||
A compact-u16 is a multi-byte encoding of 16 bits. The first byte contains the
|
||||
lower 7 bits of the value in its lower 7 bits. If the value is above 0x7f, the
|
||||
high bit is set and the next 7 bits of the value are placed into the lower 7
|
||||
bits of a second byte. If the value is above 0x3fff, the high bit is set and
|
||||
the remaining 2 bits of the value are placed into the lower 2 bits of a third
|
||||
byte.
|
||||
|
||||
### Account Address Format
|
||||
|
||||
An account address is 32-bytes of arbitrary data. When the address requires a
|
||||
digital signature, the runtime interprets it as the public key of an ed25519
|
||||
keypair.
|
||||
|
||||
## 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's owner specifies
|
||||
which loader should be used to load and execute the program and the data
|
||||
contains information about how the runtime should execute the program.
|
||||
|
||||
In the case of [deployed BPF
|
||||
programs](developing/deployed-programs/overview.md), the owner is the BPF Loader
|
||||
and the account data holds the BPF bytecode. Program accounts are permanently
|
||||
marked as executable by the loader once they are successfully deployed. The
|
||||
runtime will reject transactions that specify programs that are not executable.
|
||||
|
||||
|
||||
Unlike deployed programs, [builtins](developing/builtins/programs.md) are handled
|
||||
differently in that they are built directly into the Solana runtime.
|
||||
|
||||
### 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.
|
@ -1,98 +0,0 @@
|
||||
---
|
||||
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.
|
||||
|
||||
## Proposed 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"]],
|
||||
)?;
|
||||
```
|
||||
|
||||
### 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.
|
@ -1,156 +0,0 @@
|
||||
---
|
||||
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.
|
||||
|
||||
## Proposed 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 Program-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 has no private key associated with it, and generating
|
||||
a signature for it is impossible. While it has no private key of
|
||||
its own, it can issue an instruction that includes the Program-Address as a signer.
|
||||
|
||||
### Hash-based generated Program Addresses
|
||||
|
||||
All 256-bit values are valid ed25519 curve points and valid ed25519 public
|
||||
keys. All are equally secure and equally as hard to break.
|
||||
Based on this assumption, Program Addresses can be deterministically
|
||||
derived from a base seed using a 256-bit preimage resistant hash function.
|
||||
|
||||
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 keywords. These keywords can symbolically identify how the addresses are used.
|
||||
|
||||
```rust,ignore
|
||||
//! Generate a derived program address
|
||||
//! * seeds, symbolic keywords used to derive the key
|
||||
//! * owner, program that the key is derived for
|
||||
pub fn create_program_address(seeds: &[&str], owner: &Pubkey) -> Result<Pubkey, PubkeyError> {
|
||||
let mut hasher = Hasher::default();
|
||||
for seed in seeds.iter() {
|
||||
if seed.len() > MAX_SEED_LEN {
|
||||
return Err(PubkeyError::MaxSeedLengthExceeded);
|
||||
}
|
||||
hasher.hash(seed.as_ref());
|
||||
}
|
||||
hasher.hashv(&[owner.as_ref(), "ProgramDerivedAddress".as_ref()]);
|
||||
|
||||
Ok(Pubkey::new(hashv(&[hasher.result().as_ref()]).as_ref()))
|
||||
}
|
||||
```
|
||||
|
||||
### 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);
|
||||
let message = Message::new(vec![
|
||||
token_instruction::transfer(&alice_pubkey, &escrow_pubkey, 1),
|
||||
]);
|
||||
//transfer 1 token to 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
|
||||
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 key 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 keywords used
|
||||
to generate the address.
|
||||
|
||||
The runtime will internally call `create_program_address`, and compare the
|
||||
result against the addresses supplied in the instruction.
|
@ -8,14 +8,14 @@ rocksdb ledger will continue to serve as the primary data source, and then will
|
||||
fall back to the external data store.
|
||||
|
||||
The affected RPC endpoints are:
|
||||
* [getFirstAvailableBlock](https://docs.solana.com/apps/jsonrpc-api#getfirstavailableblock)
|
||||
* [getConfirmedBlock](https://docs.solana.com/apps/jsonrpc-api#getconfirmedblock)
|
||||
* [getConfirmedBlocks](https://docs.solana.com/apps/jsonrpc-api#getconfirmedblocks)
|
||||
* [getConfirmedSignaturesForAddress](https://docs.solana.com/apps/jsonrpc-api#getconfirmedsignaturesforaddress)
|
||||
* [getConfirmedTransaction](https://docs.solana.com/apps/jsonrpc-api#getconfirmedtransaction)
|
||||
* [getSignatureStatuses](https://docs.solana.com/apps/jsonrpc-api#getsignaturestatuses)
|
||||
* [getFirstAvailableBlock](developing/clients/jsonrpc-api.md#getfirstavailableblock)
|
||||
* [getConfirmedBlock](developing/clients/jsonrpc-api.md#getconfirmedblock)
|
||||
* [getConfirmedBlocks](developing/clients/jsonrpc-api.md#getconfirmedblocks)
|
||||
* [getConfirmedSignaturesForAddress](developing/clients/jsonrpc-api.md#getconfirmedsignaturesforaddress)
|
||||
* [getConfirmedTransaction](developing/clients/jsonrpc-api.md#getconfirmedtransaction)
|
||||
* [getSignatureStatuses](developing/clients/jsonrpc-api.md#getsignaturestatuses)
|
||||
|
||||
Note that [getBlockTime](https://docs.solana.com/apps/jsonrpc-api#getblocktime)
|
||||
Note that [getBlockTime](developing/clients/jsonrpc-api.md#getblocktime)
|
||||
is not supported, as once https://github.com/solana-labs/solana/issues/10089 is
|
||||
fixed then `getBlockTime` can be removed.
|
||||
|
||||
|
@ -1,81 +0,0 @@
|
||||
---
|
||||
title: secp256k1 builtin instruction
|
||||
---
|
||||
|
||||
## 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.
|
@ -150,11 +150,11 @@ generate a Solana keypair using any of our [wallet tools](../wallet-guide/cli.md
|
||||
|
||||
We recommend using a unique deposit account for each of your users.
|
||||
|
||||
Solana accounts are charged [rent](../apps/rent.md) on creation and once per
|
||||
Solana accounts are charged [rent](developing/programming-model/accounts.md#rent) on creation and once per
|
||||
epoch, but they can be made rent-exempt if they contain 2-years worth of rent in
|
||||
SOL. In order to find the minimum rent-exempt balance for your deposit accounts,
|
||||
query the
|
||||
[`getMinimumBalanceForRentExemption` endpoint](../apps/jsonrpc-api.md#getminimumbalanceforrentexemption):
|
||||
[`getMinimumBalanceForRentExemption` endpoint](developing/clients/jsonrpc-api.md#getminimumbalanceforrentexemption):
|
||||
|
||||
```bash
|
||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id":1,"method":"getMinimumBalanceForRentExemption","params":[0]}' localhost:8899
|
||||
@ -179,7 +179,7 @@ The easiest way to track all the deposit accounts for your exchange is to poll
|
||||
for each confirmed block and inspect for addresses of interest, using the
|
||||
JSON-RPC service of your Solana api node.
|
||||
|
||||
- To identify which blocks are available, send a [`getConfirmedBlocks` request](../apps/jsonrpc-api.md#getconfirmedblocks),
|
||||
- To identify which blocks are available, send a [`getConfirmedBlocks` request](developing/clients/jsonrpc-api.md#getconfirmedblocks),
|
||||
passing the last block you have already processed as the start-slot parameter:
|
||||
|
||||
```bash
|
||||
@ -190,7 +190,7 @@ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id":1,"m
|
||||
|
||||
Not every slot produces a block, so there may be gaps in the sequence of integers.
|
||||
|
||||
- For each block, request its contents with a [`getConfirmedBlock` request](../apps/jsonrpc-api.md#getconfirmedblock):
|
||||
- For each block, request its contents with a [`getConfirmedBlock` request](developing/clients/jsonrpc-api.md#getconfirmedblock):
|
||||
|
||||
```bash
|
||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id":1,"method":"getConfirmedBlock","params":[5, "json"]}' localhost:8899
|
||||
@ -273,7 +273,7 @@ can request the block from RPC in binary format, and parse it using either our
|
||||
|
||||
You can also query the transaction history of a specific address.
|
||||
|
||||
- Send a [`getConfirmedSignaturesForAddress`](../apps/jsonrpc-api.md#getconfirmedsignaturesforaddress)
|
||||
- Send a [`getConfirmedSignaturesForAddress`](developing/clients/jsonrpc-api.md#getconfirmedsignaturesforaddress)
|
||||
request to the api node, specifying a range of recent slots:
|
||||
|
||||
```bash
|
||||
@ -291,7 +291,7 @@ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id":1,"m
|
||||
```
|
||||
|
||||
- For each signature returned, get the transaction details by sending a
|
||||
[`getConfirmedTransaction`](../apps/jsonrpc-api.md#getconfirmedtransaction) request:
|
||||
[`getConfirmedTransaction`](developing/clients/jsonrpc-api.md#getconfirmedtransaction) request:
|
||||
|
||||
```bash
|
||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id":1,"method":"getConfirmedTransaction","params":["dhjhJp2V2ybQGVfELWM1aZy98guVVsxRCB5KhNiXFjCBMK5KEyzV8smhkVvs3xwkAug31KnpzJpiNPtcD5bG1t6", "json"]}' localhost:8899
|
||||
@ -382,13 +382,14 @@ For greater flexibility, you can submit withdrawal transfers asynchronously. In
|
||||
these cases, it is your responsibility to verify that the transaction succeeded
|
||||
and was finalized by the cluster.
|
||||
|
||||
**Note:** Each transaction contains a [recent blockhash](../transaction.md#blockhash-format)
|
||||
to indicate its liveness. It is **critical** to wait until this blockhash
|
||||
expires before retrying a withdrawal transfer that does not appear to have been
|
||||
**Note:** Each transaction contains a [recent
|
||||
blockhash](developing/programming-model/transactions.md#blockhash-format) to
|
||||
indicate its liveness. It is **critical** to wait until this blockhash expires
|
||||
before retrying a withdrawal transfer that does not appear to have been
|
||||
confirmed or finalized by the cluster. Otherwise, you risk a double spend. See
|
||||
more on [blockhash expiration](#blockhash-expiration) below.
|
||||
|
||||
First, get a recent blockhash using the [`getFees` endpoint](../apps/jsonrpc-api.md#getfees)
|
||||
First, get a recent blockhash using the [`getFees` endpoint](developing/clients/jsonrpc-api.md#getfees)
|
||||
or the CLI command:
|
||||
|
||||
```bash
|
||||
@ -403,12 +404,12 @@ solana transfer <USER_ADDRESS> <AMOUNT> --no-wait --blockhash <RECENT_BLOCKHASH>
|
||||
```
|
||||
|
||||
You can also build, sign, and serialize the transaction manually, and fire it off to
|
||||
the cluster using the JSON-RPC [`sendTransaction` endpoint](../apps/jsonrpc-api.md#sendtransaction).
|
||||
the cluster using the JSON-RPC [`sendTransaction` endpoint](developing/clients/jsonrpc-api.md#sendtransaction).
|
||||
|
||||
#### Transaction Confirmations & Finality
|
||||
|
||||
Get the status of a batch of transactions using the
|
||||
[`getSignatureStatuses` JSON-RPC endpoint](../apps/jsonrpc-api.md#getsignaturestatuses).
|
||||
[`getSignatureStatuses` JSON-RPC endpoint](developing/clients/jsonrpc-api.md#getsignaturestatuses).
|
||||
The `confirmations` field reports how many
|
||||
[confirmed blocks](../terminology.md#confirmed-block) have elapsed since the
|
||||
transaction was processed. If `confirmations: null`, it is [finalized](../terminology.md#finality).
|
||||
@ -448,15 +449,15 @@ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "
|
||||
#### Blockhash Expiration
|
||||
|
||||
When you request a recent blockhash for your withdrawal transaction using the
|
||||
[`getFees` endpoint](../apps/jsonrpc-api.md#getfees) or `solana fees`, the
|
||||
[`getFees` endpoint](developing/clients/jsonrpc-api.md#getfees) or `solana fees`, the
|
||||
response will include the `lastValidSlot`, the last slot in which the blockhash
|
||||
will be valid. You can check the cluster slot with a
|
||||
[`getSlot` query](../apps/jsonrpc-api.md#getslot); once the cluster slot is
|
||||
[`getSlot` query](developing/clients/jsonrpc-api.md#getslot); once the cluster slot is
|
||||
greater than `lastValidSlot`, the withdrawal transaction using that blockhash
|
||||
should never succeed.
|
||||
|
||||
You can also doublecheck whether a particular blockhash is still valid by sending a
|
||||
[`getFeeCalculatorForBlockhash`](../apps/jsonrpc-api.md#getfeecalculatorforblockhash)
|
||||
[`getFeeCalculatorForBlockhash`](developing/clients/jsonrpc-api.md#getfeecalculatorforblockhash)
|
||||
request with the blockhash as a parameter. If the response value is null, the
|
||||
blockhash is expired, and the withdrawal transaction should never succeed.
|
||||
|
||||
@ -550,10 +551,10 @@ SPL Token accounts are queried and modified using the `spl-token` command line
|
||||
utility. The examples provided in this section depend upon having it installed
|
||||
on the local system.
|
||||
|
||||
`spl-token` is distributed from Rust [crates.io](https://crates.io) via the Rust
|
||||
`cargo` command line utility. The latest version of `cargo` can be installed
|
||||
using a handy one-liner for your platform at [rustup.rs](https://rustup.rs). Once
|
||||
`cargo` is installed, `spl-token` can be obtained with the following command:
|
||||
`spl-token` is distributed from Rust [crates.io](https://crates.io/crates/spl-token)
|
||||
via the Rust `cargo` command line utility. The latest version of `cargo` can be
|
||||
installed using a handy one-liner for your platform at [rustup.rs](https://rustup.rs).
|
||||
Once `cargo` is installed, `spl-token` can be obtained with the following command:
|
||||
|
||||
```
|
||||
cargo install spl-token-cli
|
||||
@ -580,7 +581,7 @@ accounts do not:
|
||||
deposited. Token accounts can be created explicitly with the
|
||||
`spl-token create-account` command, or implicitly by the
|
||||
`spl-token transfer --fund-recipient ...` command.
|
||||
1. SPL Token accounts must remain [rent-exempt](https://docs.solana.com/apps/rent#rent-exemption)
|
||||
1. SPL Token accounts must remain [rent-exempt](developing/programming-model/accounts.md#rent-exemption)
|
||||
for the duration of their existence and therefore require a small amount of
|
||||
native SOL tokens be deposited at account creation. For SPL Token v2 accounts,
|
||||
this amount is 0.00203928 SOL (2,039,280 lamports).
|
||||
@ -657,7 +658,7 @@ method described above. Each new block should be scanned for successful transact
|
||||
issuing SPL Token [Transfer](https://github.com/solana-labs/solana-program-library/blob/096d3d4da51a8f63db5160b126ebc56b26346fc8/token/program/src/instruction.rs#L92)
|
||||
or [Transfer2](https://github.com/solana-labs/solana-program-library/blob/096d3d4da51a8f63db5160b126ebc56b26346fc8/token/program/src/instruction.rs#L252)
|
||||
instructions referencing user accounts, then querying the
|
||||
[token account balance](https://docs.solana.com/apps/jsonrpc-api#gettokenaccountbalance)
|
||||
[token account balance](developing/clients/jsonrpc-api.md#gettokenaccountbalance)
|
||||
updates.
|
||||
|
||||
[Considerations](https://github.com/solana-labs/solana/issues/12318) are being
|
||||
|
@ -3,7 +3,7 @@ title: Durable Transaction Nonces
|
||||
---
|
||||
|
||||
Durable transaction nonces are a mechanism for getting around the typical
|
||||
short lifetime of a transaction's [`recent_blockhash`](../transaction.md#recent-blockhash).
|
||||
short lifetime of a transaction's [`recent_blockhash`](developing/programming-model/transactions.md#recent-blockhash).
|
||||
They are implemented as a Solana Program, the mechanics of which can be read
|
||||
about in the [proposal](../implemented-proposals/durable-tx-nonces.md).
|
||||
|
||||
@ -48,7 +48,7 @@ solana create-nonce-account nonce-keypair.json 1
|
||||
2SymGjGV4ksPdpbaqWFiDoBz8okvtiik4KE9cnMQgRHrRLySSdZ6jrEcpPifW4xUpp4z66XM9d9wM48sA7peG2XL
|
||||
```
|
||||
|
||||
> To keep the keypair entirely offline, use the [Paper Wallet](../wallet-guide/paper-wallet.md) keypair generation [instructions](../paper-wallet/paper-wallet-usage.md#seed-phrase-generation.md) instead
|
||||
> To keep the keypair entirely offline, use the [Paper Wallet](wallet-guide/paper-wallet.md) keypair generation [instructions](wallet-guide/paper-wallet.md#seed-phrase-generation) instead
|
||||
|
||||
> [Full usage documentation](../cli/usage.md#solana-create-nonce-account)
|
||||
|
||||
|
@ -8,8 +8,8 @@ import styles from "./styles.module.css";
|
||||
|
||||
const features = [
|
||||
{
|
||||
title: <>⛏ Build Your First App</>,
|
||||
imageUrl: "https://github.com/solana-labs/example-helloworld",
|
||||
title: <>⛏ Start Building</>,
|
||||
imageUrl: "developing/programming-model/overview",
|
||||
description: <>Get started building your decentralized app or marketplace.</>,
|
||||
},
|
||||
{
|
||||
|
@ -71,7 +71,7 @@ account.
|
||||
This is a normal transaction so the standard transaction fee will apply. The
|
||||
transaction fee range is defined by the genesis block. The actual fee will
|
||||
fluctuate based on transaction load. You can determine the current fee via the
|
||||
[RPC API “getRecentBlockhash”](../apps/jsonrpc-api.md#getrecentblockhash)
|
||||
[RPC API “getRecentBlockhash”](developing/clients/jsonrpc-api.md#getrecentblockhash)
|
||||
before submitting a transaction.
|
||||
|
||||
Learn more about [transaction fees here](../implemented-proposals/transaction-fees.md).
|
||||
|
@ -141,7 +141,7 @@ solana-keygen pubkey ASK
|
||||
|
||||
and then entering your seed phrase.
|
||||
|
||||
See [Paper Wallet Usage](../paper-wallet/paper-wallet-usage.md) for more info.
|
||||
See [Paper Wallet Usage](../wallet-guide/paper-wallet.md) for more info.
|
||||
|
||||
---
|
||||
|
||||
@ -261,7 +261,7 @@ To force validator logging to the console add a `--log -` argument, otherwise
|
||||
the validator will automatically log to a file.
|
||||
|
||||
> Note: You can use a
|
||||
> [paper wallet seed phrase](../paper-wallet/paper-wallet-usage.md)
|
||||
> [paper wallet seed phrase](../wallet-guide/paper-wallet.md)
|
||||
> for your `--identity` and/or
|
||||
> `--vote-account` keypairs. To use these, pass the respective argument as
|
||||
> `solana-validator --identity ASK ... --vote-account ASK ...` and you will be
|
||||
|
@ -210,6 +210,10 @@ The component of a [validator](terminology.md#validator) responsible for [progra
|
||||
|
||||
A fraction of a [block](terminology.md#block); the smallest unit sent between [validators](terminology.md#validator).
|
||||
|
||||
## signature
|
||||
|
||||
A 64-byte ed25519 signature of R (32-bytes) and S (32-bytes). With the requirement that R is a packed Edwards point not of small order and S is a scalar in the range of 0 <= S < L.
|
||||
|
||||
## slot
|
||||
|
||||
The period of time for which a [leader](terminology.md#leader) ingests transactions and produces a [block](terminology.md#block).
|
||||
|
@ -1,77 +0,0 @@
|
||||
---
|
||||
title: Anatomy of a Transaction
|
||||
---
|
||||
|
||||
This section documents the binary format of a transaction.
|
||||
|
||||
## Transaction Format
|
||||
|
||||
A transaction contains a [compact-array](#compact-array-format) of signatures,
|
||||
followed by a [message](#message-format). Each item in the signatures array is
|
||||
a [digital signature](#signature-format) of the given message. The Solana
|
||||
runtime verifies that the number of signatures matches the number in the first
|
||||
8 bits of the [message header](#message-header-format). It also verifies that
|
||||
each signature was signed by the private key corresponding to the public key at
|
||||
the same index in the message's account addresses array.
|
||||
|
||||
### Signature Format
|
||||
|
||||
Each digital signature is in the ed25519 binary format and consumes 64 bytes.
|
||||
|
||||
## Message Format
|
||||
|
||||
A message contains a [header](#message-header-format), followed by a
|
||||
compact-array of [account addresses](#account-addresses-format), followed by a
|
||||
recent [blockhash](#blockhash-format), followed by a compact-array of
|
||||
[instructions](#instruction-format).
|
||||
|
||||
### Message Header Format
|
||||
|
||||
The message header contains three unsigned 8-bit values. The first value is the
|
||||
number of required signatures in the containing transaction. The second value
|
||||
is the number of those corresponding account addresses that are read-only. The
|
||||
third value in the message header is the number of read-only account addresses
|
||||
not requiring signatures.
|
||||
|
||||
### Account Addresses Format
|
||||
|
||||
The addresses that require signatures appear at the beginning of the account
|
||||
address array, with addresses requesting write access first and read-only
|
||||
accounts following. The addresses that do not require signatures follow the
|
||||
addresses that do, again with read-write accounts first and read-only accounts
|
||||
following.
|
||||
|
||||
### Blockhash Format
|
||||
|
||||
A blockhash contains a 32-byte SHA-256 hash. It is used to indicate when a
|
||||
client last observed the ledger. Validators will reject transactions when the
|
||||
blockhash is too old.
|
||||
|
||||
## Instruction Format
|
||||
|
||||
An instruction contains a program ID index, followed by a compact-array of
|
||||
account address indexes, followed by a compact-array of opaque 8-bit data. The
|
||||
program ID index is used to identify an on-chain program that can interpret the
|
||||
opaque data. The program ID index is an unsigned 8-bit index to an account
|
||||
address in the message's array of account addresses. The account address
|
||||
indexes are each an unsigned 8-bit index into that same array.
|
||||
|
||||
## Compact-Array Format
|
||||
|
||||
A compact-array is serialized as the array length, followed by each array item.
|
||||
The array length is a special multi-byte encoding called compact-u16.
|
||||
|
||||
### Compact-u16 Format
|
||||
|
||||
A compact-u16 is a multi-byte encoding of 16 bits. The first byte contains the
|
||||
lower 7 bits of the value in its lower 7 bits. If the value is above 0x7f, the
|
||||
high bit is set and the next 7 bits of the value are placed into the lower 7
|
||||
bits of a second byte. If the value is above 0x3fff, the high bit is set and
|
||||
the remaining 2 bits of the value are placed into the lower 2 bits of a third
|
||||
byte.
|
||||
|
||||
## Account Address Format
|
||||
|
||||
An account address is 32-bytes of arbitrary data. When the address requires a
|
||||
digital signature, the runtime interprets it as the public key of an ed25519
|
||||
keypair.
|
@ -84,7 +84,7 @@ simply click Logout and re-connect with the correct address.
|
||||
|
||||
## Select a Network
|
||||
|
||||
Solana maintains [three distinct networks](../clusters.md), each of which has
|
||||
Solana maintains [three distinct networks](../clusters), each of which has
|
||||
its own purpose in supporting the Solana ecosystem. Mainnet Beta is selected by
|
||||
default on SolFlare, as this is the permanent network where exchanges and other
|
||||
production apps are deployed. To select a different network, click on the name
|
||||
@ -113,7 +113,7 @@ and then it will be submitted to the network.
|
||||
## Staking SOL Tokens
|
||||
SolFlare supports creating and managing stake accounts and delegations. To learn
|
||||
about how staking on Solana works in general, check out our
|
||||
[Staking Guide](../staking.md).
|
||||
[Staking Guide](../staking).
|
||||
|
||||
### Create a Stake Account
|
||||
You can use some of the SOL tokens in your wallet to create a new stake account.
|
||||
@ -129,7 +129,7 @@ After you submit and [sign the transaction](#signing-a-transaction) you will see
|
||||
your new stake account appear in the box labeled "Your Staking Accounts".
|
||||
|
||||
Stake accounts created on SolFlare set your wallet address as the
|
||||
[staking and withdrawing authority](../staking/stake-accounts.md#understanding-account-authorities)
|
||||
[staking and withdrawing authority](../staking/stake-accounts#understanding-account-authorities)
|
||||
for your new account, which gives your wallet's key the authority to sign
|
||||
for any transactions related to the new stake account.
|
||||
|
||||
@ -141,13 +141,13 @@ exist at a different address from your wallet.
|
||||
SolFlare will locate any display all stake accounts on the
|
||||
[selected network](#select-a-network)
|
||||
for which your wallet address is assigned as the
|
||||
[stake authority](../staking/stake-accounts.md#understanding-account-authorities).
|
||||
[stake authority](../staking/stake-accounts#understanding-account-authorities).
|
||||
Stake accounts that were created outside of SolFlare will also be displayed and
|
||||
can be managed as long as the wallet you logged in with is assigned as the stake
|
||||
authority.
|
||||
|
||||
### Delegate tokens in a Stake Account
|
||||
Once you have [selected a validator](../staking.md#select-a-validator), you may
|
||||
Once you have [selected a validator](../staking#select-a-validator), you may
|
||||
delegate the tokens in one of your stake accounts to them. From the Staking
|
||||
dashboard, click "Delegate" at the right side of a displayed stake account.
|
||||
Select the validator you wish to delegate to from the drop down list and click
|
||||
|
Reference in New Issue
Block a user