程序

在 Solana 上,智能合约被称为程序。程序是一种无状态的 账户,其中包含可执行代码。这些代码被组织为称为指令的函数。用户通过发送包含一个或多个 指令交易 与程序进行交互。一笔交易可以包含来自多个程序的指令。

当程序被部署时,Solana 会使用 LLVM 将其编译为可执行与可链接格式(ELF)。ELF 文件包含以 Solana 字节码格式(sBPF)编译的程序二进制文件,并保存在链上的可执行账户中。

sBPF 是 Solana 定制的 eBPF 字节码版本。

编写程序

大多数程序都是用 Rust 编写的,常见的开发方式有两种:

  • Anchor:Anchor 是为 Solana 快速、便捷开发而设计的框架。它利用 Rust 宏 来减少样板代码,非常适合初学者。
  • 原生 Rust:直接用 Rust 编写程序,不依赖任何框架。这种方式更灵活,但复杂度也更高。

更新程序

修改现有程序,必须有一个账户被指定为升级权限账户。(通常是最初部署程序的账户。)如果升级权限被撤销并设置为 None,则该程序将无法再被更新。

验证程序

Solana 支持可验证构建,用户可以检查链上程序代码是否与其公开源代码一致。Anchor 框架提供了内置支持来创建可验证构建。

要检查现有程序是否已验证,请在 Solana Explorer 上搜索其 program ID。或者,你也可以使用 Ellipsis Labs 的 Solana Verifiable Build CLI,独立验证链上程序。

内置程序

The System Program

System Program 是唯一可以创建新账户的账户。默认情况下,所有新账户都归 System Program 所有,尽管许多账户在创建时会被分配给新的所有者。System Program 执行以下关键功能:

功能描述
新账户创建只有 System Program 可以创建新账户。
空间分配设置每个账户数据字段的字节容量。
分配程序所有权System Program 创建账户后,可以将指定的程序所有者重新分配给其他 program account。自定义程序就是通过这种方式获得 System Program 创建的新账户的所有权。
转账 SOL将 lamports(SOL)从 System Program 账户转移到其他账户。

system program 的地址是 11111111111111111111111111111111

Loader 程序

每个程序都归另一个账户所有——即其 loader。Loader 用于部署、重新部署、升级或关闭程序,也用于完成程序和转移程序权限。

Loader 程序有时也被称为“BPF Loaders”。

目前有五个 loader 程序,如下表所示。

LoaderProgram ID说明指令链接
nativeNativeLoader1111111111111111111111111111111拥有其他四个 loader
v1BPFLoader1111111111111111111111111111111111管理指令已禁用,但程序仍可执行
v2BPFLoader2111111111111111111111111111111111管理指令已禁用,但程序仍可执行指令
v3BPFLoaderUpgradeab1e11111111111111111111111程序部署后可更新。可执行文件存储在单独的 program data account 中指令
v4LoaderV411111111111111111111111111111111111开发中(未发布)指令

使用 loader-v3 或 loader-v4 部署的程序,在部署后可能仍可修改,具体取决于其升级权限。

当新程序部署时,默认会使用最新的 loader 版本。

预编译程序

除了 loader 程序外,Solana 还提供以下预编译程序。

验证 ed25519 签名

ed25519 程序用于验证一个或多个 ed25519 签名。

程序程序 ID描述指令
Ed25519 程序Ed25519SigVerify111111111111111111111111111验证 ed25519 签名。如果有任何签名验证失败,则返回错误。指令

ed25519 程序会处理一个指令。指令的第一个 u8 包含要验证的签名数量,后跟一个单字节填充。之后,以下结构会被序列化,每个待验证签名各有一个。

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
}
Signature verification pseudocode
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 公钥恢复

secp256k1 程序用于验证 secp256k1 公钥恢复操作。

程序程序 ID描述指令
Secp256k1 程序KeccakSecp256k11111111111111111111111111111验证 secp256k1 公钥恢复操作(ecrecover)。指令

secp256k1 程序会处理一个指令。指令的第一个字节包含要验证的公钥数量。之后,以下结构会为每个公钥创建一次,然后序列化并添加到 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
}
Recovery verification pseudocode
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
}

这允许用户在交易中为签名和消息数据指定任意 instruction data。通过指定特殊的 instructions sysvar,还可以从交易本身接收数据。

交易成本将按需验证的签名数量乘以签名验证成本系数计算。

secp256r1 程序用于验证最多 8 个 secp256r1 签名。

程序程序 ID描述指令
Secp256r1 程序Secp256r1SigVerify1111111111111111111111111验证最多 8 个 secp256r1 签名。接收签名、公钥和消息。如果有任何验证失败,则返回错误。指令

secp256r1 程序会处理一个指令。指令的第一个 u8 是待验证签名的数量,后跟一个单字节填充。之后,为每个签名创建以下结构体,然后序列化并添加到 instruction data。

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
}
所有签名都强制使用低 S 值,以避免意外的签名可变性。
Signature verification psuedocode
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
}

核心程序

下表中的程序为网络提供核心功能。

程序程序 ID描述指令
System11111111111111111111111111111111创建新账户、分配账户数据、将账户分配给所属程序、从 System Program 拥有的账户转移 lamports,并支付交易费用SystemInstruction
VoteVote111111111111111111111111111111111111111创建和管理跟踪 validator 投票状态和奖励的账户VoteInstruction
StakeStake11111111111111111111111111111111111111创建和管理代表委托给 validator 的质押和奖励的账户StakeInstruction
ConfigConfig1111111111111111111111111111111111111向链中添加配置信息,并指定允许修改该配置的公钥列表。与其他程序不同,Config 程序没有定义任何单独的指令。它只有一个隐式指令:“store”。其 instruction data 是一组控制账户访问权限和存储数据的密钥ConfigInstruction
Compute BudgetComputeBudget111111111111111111111111111111设置交易的计算单元限制和价格,允许用户控制计算资源和优先级费用ComputeBudgetInstruction
Address Lookup TableAddressLookupTab1e1111111111111111111111111管理地址查找表,使交易能够引用比账户列表中可容纳的更多账户ProgramInstruction
ZK ElGamal ProofZkE1Gama1Proof11111111111111111111111111111为 ElGamal 加密数据提供零知识证明验证

Is this page helpful?

Table of Contents

Edit Page

管理者

©️ 2026 Solana 基金会版权所有
取得联系