Programs

On Solana, "smart contracts" are called programs. Programs are deployed on-chain to accounts that contain the program's compiled executable binary. Users interact with programs by sending transactions containing instructions that tell the program what to do.

Key Points

  • Programs are accounts containing executable code, organized into functions called instructions.
  • While programs are stateless, they can include instructions that create and update other accounts to store data.
  • An upgrade authority can update programs. Once this authority is removed, the program becomes immutable.
  • Users can verify an on-chain program account's data matches its public source code through verifiable builds.

Writing Solana Programs

Solana programs are predominantly written in the Rust programming language, with two common approaches for development:

  • Anchor: A framework designed for Solana program development. It provides a faster and simpler way to write programs, using Rust macros to reduce boilerplate code. For beginners, it is recommended to start with the Anchor framework.

  • Native Rust: This approach involves writing Solana programs in Rust without leveraging any frameworks. It offers more flexibility but comes with increased complexity.

Updating Solana Programs

To learn more about deploying and upgrading programs, see the deploying programs page.

Programs can be directly modified by an account designated as the "upgrade authority", which is typically the account that originally deployed the program. If the upgrade authority is revoked and set to None, the program becomes immutable and can no longer be updated.

Verifiable Programs

Verifiable builds allow anyone to check if a program's on-chain code matches its public source code, making it possible to detect discrepancies between source and deployed versions.

The Solana developer community has introduced tools to support verifiable builds, enabling both developers and users to verify that on-chain programs accurately reflect their publicly shared source code.

  • Searching for Verified Programs: To quickly check for verified programs, users can search for a program address on Solana Explorer. View an example of a verified program here.

  • Verification Tools: The Solana Verifiable Build CLI by Ellipsis Labs enables users to independently verify on-chain programs against published source code.

  • Support for Verifiable Builds in Anchor: Anchor provides built-in support for verifiable builds. Details can be found in the Anchor documentation.

Berkeley Packet Filter (BPF)

Solana uses LLVM (Low Level Virtual Machine) to compile programs into ELF (Executable and Linkable Format) files. These files contain Solana's custom version of eBPF bytecode, called "Solana Bytecode Format" (sBPF). The ELF file contains the program's binary and is stored on-chain in an executable account when the program is deployed.

Built-in Programs

Loader Programs

Every program itself is owned by another program, which is its loader. Currently, five loaders programs exist:

LoaderProgram IDNotesInstructions Link
nativeNativeLoader1111111111111111111111111111111Owns the other four loaders
v1BPFLoader1111111111111111111111111111111111Management instructions are disabled, but programs still execute
v2BPFLoader2111111111111111111111111111111111Management instructions are disabled, but programs still executeInstructions
v3BPFLoaderUpgradeab1e11111111111111111111111Is being phased outInstructions
v4LoaderV411111111111111111111111111111111111v4 is expected to become the standard loaderInstructions

These loaders are necessary to create and manage custom programs:

  • Deploy a new program or buffer
  • Close a program or buffer
  • Redeploy / upgrade an existing program
  • Transfer the authority over a program
  • Finalize a program

Loader-v3 and loader-v4 support modifications to programs after their initial deployment. Permission to do so is regulated by the authority of a program because the account ownership of each program resides with the loader.

Precompiled Programs

Ed25519 Program

ProgramProgram IDDescriptionInstructions
Ed25519 ProgramEd25519SigVerify111111111111111111111111111Verifies ed25519 signatures. If any signature fails, an error is returned.Instructions

The ed25519 program processes an instruction. The first u8 is a count of the number of signatures to check, which is followed by a single byte padding. After that, the following struct is serialized, one for each signature to check.

Ed25519SignatureOffsets
struct Ed25519SignatureOffsets {
signature_offset: u16, // offset to ed25519 signature of 64 bytes
signature_instruction_index: u16, // instruction index to find signature
public_key_offset: u16, // offset to public key of 32 bytes
public_key_instruction_index: u16, // instruction index to find public key
message_data_offset: u16, // offset to start of message data
message_data_size: u16, // size of message data
message_instruction_index: u16, // index of instruction data to get message data
}

The pseudo code of the signature verification:

process_instruction() {
for i in 0..count {
// i'th index values referenced:
instructions = &transaction.message().instructions
instruction_index = ed25519_signature_instruction_index != u16::MAX ? ed25519_signature_instruction_index : current_instruction;
signature = instructions[instruction_index].data[ed25519_signature_offset..ed25519_signature_offset + 64]
instruction_index = ed25519_pubkey_instruction_index != u16::MAX ? ed25519_pubkey_instruction_index : current_instruction;
pubkey = instructions[instruction_index].data[ed25519_pubkey_offset..ed25519_pubkey_offset + 32]
instruction_index = ed25519_message_instruction_index != u16::MAX ? ed25519_message_instruction_index : current_instruction;
message = instructions[instruction_index].data[ed25519_message_data_offset..ed25519_message_data_offset + ed25519_message_data_size]
if pubkey.verify(signature, message) != Success {
return Error
}
}
return Success
}

Secp256k1 Program

ProgramProgram IDDescriptionInstructions
Secp256k1 ProgramKeccakSecp256k11111111111111111111111111111Verifies secp256k1 public key recovery operations (ecrecover).Instructions

The secp256k1 program processes an instruction which takes in as the first byte a count of the following struct serialized in the instruction data:

Secp256k1SignatureOffsets
struct Secp256k1SignatureOffsets {
secp_signature_offset: u16, // offset to [signature,recovery_id] of 64+1 bytes
secp_signature_instruction_index: u8, // instruction index to find signature
secp_pubkey_offset: u16, // offset to ethereum_address pubkey of 20 bytes
secp_pubkey_instruction_index: u8, // instruction index to find pubkey
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, // instruction index to find message data
}

The pseudo code of the recovery verification:

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 + 20]
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.

Secp256r1 Program

ProgramProgram IDDescriptionInstructions
Secp256r1 ProgramSecp256r1SigVerify1111111111111111111111111Verifies up to 8 secp256r1 signatures. Takes a signature, public key, and message. Returns error if any fail.Instructions

The secp256r1 program processes an instruction. The first u8 is a count of the number of signatures to check, followed by a single byte padding. After that, the following struct is serialized, one for each signature to check:

Secp256r1SignatureOffsets
struct Secp256r1SignatureOffsets {
signature_offset: u16, // offset to compact secp256r1 signature of 64 bytes
signature_instruction_index: u16, // instruction index to find signature
public_key_offset: u16, // offset to compressed public key of 33 bytes
public_key_instruction_index: u16, // instruction index to find public key
message_data_offset: u16, // offset to start of message data
message_data_size: u16, // size of message data
message_instruction_index: u16, // index of instruction data to get message data
}

The pseudo code of the signature verification:

process_instruction() {
if data.len() < SIGNATURE_OFFSETS_START {
return Error
}
num_signatures = data[0] as usize
if num_signatures == 0 || num_signatures > 8 {
return Error
}
expected_data_size = num_signatures * SIGNATURE_OFFSETS_SERIALIZED_SIZE + SIGNATURE_OFFSETS_START
if data.len() < expected_data_size {
return Error
}
for i in 0..num_signatures {
offsets = parse_signature_offsets(data, i)
signature = get_data_slice(data, instruction_datas, offsets.signature_instruction_index, offsets.signature_offset, SIGNATURE_SERIALIZED_SIZE)
if s > half_curve_order {
return Error
}
pubkey = get_data_slice(data, instruction_datas, offsets.public_key_instruction_index, offsets.public_key_offset, COMPRESSED_PUBKEY_SERIALIZED_SIZE)
message = get_data_slice(data, instruction_datas, offsets.message_instruction_index, offsets.message_data_offset, offsets.message_data_size)
if !verify_signature(signature, pubkey, message) {
return Error
}
}
return Success
}

Note: Low S values are enforced for all signatures to avoid accidental signature malleability.

Core Programs

The Solana cluster genesis includes a list of special programs that provide core functionalities for the network. Historically these were referred to as "native" programs and they used to be distributed together with the validator code.

ProgramProgram IDDescriptionInstructions
System Program11111111111111111111111111111111Create new accounts, allocate account data, assign accounts to owning programs, transfer lamports from System Program owned accounts, and pay transaction fees.SystemInstruction
Vote ProgramVote111111111111111111111111111111111111111Create and manage accounts that track validator voting state and rewards.VoteInstruction
Stake ProgramStake11111111111111111111111111111111111111Create and manage accounts representing stake and rewards for delegations to validators.StakeInstruction
Config ProgramConfig1111111111111111111111111111111111111Add configuration data to the chain, followed by the list of public keys that are allowed to modify it. Unlike the other programs, the Config program does not define any individual instructions. It has just one implicit instruction: "store". Its instruction data is a set of keys that gate access to the account and the data to store inside of it.ConfigInstruction
Compute Budget ProgramComputeBudget111111111111111111111111111111Set compute unit limits and prices for transactions, allowing users to control compute resources and prioritization fees.ComputeBudgetInstruction
Address Lookup Table ProgramAddressLookupTab1e1111111111111111111111111Manage address lookup tables, which allow transactions to reference more accounts than would otherwise fit in the transaction's account list.ProgramInstruction
ZK ElGamal Proof ProgramZkE1Gama1Proof11111111111111111111111111111Provides zero-knowledge proof verification for ElGamal-encrypted data.

Is this page helpful?

Indice

Modifica Pagina