Solana 中的 Owner 是负责修改账户数据的程序,而 Authority 是通过签名触发操作的钱包,二者共同定义了数据控制与权限的机制。
Solana 的初学者常对 “Owner(所有者)” 和 “Authority(权限)” 的区别感到困惑。本文旨在简洁清晰地解释这两个概念,帮助开发者理解其在 Solana 生态中的作用和实现方式,同时结合示例和对比加深理解。
在 Solana 中,只有程序能修改账户数据,且仅限于其拥有的账户。程序无法随意写入其他账户的数据。
然而,程序本身不会主动操作,需要外部指令触发。这些指令通常来自特定的钱包地址,即 Authority。简单来说:
账户在 Solana 中具有以下字段:
运行以下命令可查看:
solana account 5NhLjdFKocoRMqic9sqAe5TxLagJCoCBunzg51ioMYot
输出:
Public Key: 5NhLjdFKocoRMqic9sqAe5TxLagJCoCBunzg51ioMYot
Balance: 499999998.582436085 SOL
Owner: 11111111111111111111111111111111
Executable: false
钱包的 Owner 是系统程序(111...111),而非用户自身。这是因为只有 Owner 能改数据,用户通过签名交易请求系统程序更新余额。这种模式在 Solana 中反复出现:Authority 提供签名,Owner 执行修改。
通过一个初始化程序验证 Owner 的行为。在测试中添加:
console.log(`program: ${program.programId.toBase58()}`);
console.log(`storage account: ${myStorage.toBase58()}`);
运行结果为:
other_write
program: 26Kiu5LSV5xXDN3yGwE8L6aU59kKRKdyyKtSQv5Vu5VC
storage account: 5N1mVpqBqK7LjZ768DMc9ngWso6XkieEHQmC636jEtgw
✔ Is initialized! (691ms)
然后运行solana account <storage account>
:
solana account 5N1mVpqBqK7LjZ768DMc9ngWso6XkieEHQmC636jEtgw
可以看到 owner 是程序地址:
Public Key: 5N1mVpqBqK7LjZ768DMc9ngWso6XkieEHQmC636jEtgw
Balance: 0.00100224 SOL
Owner: 26Kiu5LSV5xXDN3yGwE8L6aU59kKRKdyyKtSQv5Vu5VC
Executable: false
Rent Epoch: 18446744073709551615
Length: 16 (0x10) bytes
0000: 1c f2 3b 85 43 19 31 28 00 00 00 00 00 00 00 00 ..;.C.1(........
程序拥有存储账户,所以能够向存储账户中写入数据。用户只能通过交易请求程序写入数据。
在 Solidity 中,owner 通常指的是拥有智能合约管理权限的特殊地址。这一概念并非以太坊运行时层面的原生特性,而是 Solidity 合约中常见的一种设计模式。与此相对,Solana 中的 owner 概念则更为基础。在以太坊上,智能合约仅限于修改自身的存储槽。然而,如果存在一种机制,允许智能合约写入其他存储槽,那么在 Solana 的术语体系中,这些存储槽的归属地址就会被称为它们的 owner。
Authority 在 Solana 中既可是程序级权限,也可是账户级权限。例如:
#[account]
pub struct Player {
points: u32,
authority: Pubkey
}
这里,authority 存储在账户中,限制特定钱包的操作权限。
在程序部署中,Authority 还有另一身份:部署者钱包默认拥有升级权限。这通过运行时机制实现。
部署程序时,如何确定升级权限?以实际操作为例:
运行命令:
solana address
输出我们的钱包地址:5NhLjdFKocoRMqic9sqAe5TxLagJCoCBunzg51ioMYot。
部署程序:
anchor init owner_authority
cd owner_authority
anchor build
anchor test --skip-local-validator
日志显示程序地址:BvRvgPkfWS2bhaD8MXfJWS7rejUyfWHpcTM3QogjZ8qy。
检查:
solana account BvRvgPkfWS2bhaD8MXfJWS7rejUyfWHpcTM3QogjZ8qy
输出:
Public Key: BvRvgPkfWS2bhaD8MXfJWS7rejUyfWHpcTM3QogjZ8qy
Balance: 0.00114144 SOL
Owner: BPFLoaderUpgradeab1e11111111111111111111111
Executable: true
Rent Epoch: 18446744073709551615
Length: 36 (0x24) bytes
0000: 02 00 00 00 38 9f d3 e7 34 26 4a 61 fd a8 7d 6e ....8...4&Ja..}n
0010: 63 af 3e 04 2d 0d 4f 28 72 5f 23 15 9f 23 e2 63 c.>.-.O(r_#..#.c
0020: 60 07 41 91 `.A.
Owner 是 BPFLoaderUpgradeable,Authority 呢?运行:
solana program show BvRvgPkfWS2bhaD8MXfJWS7rejUyfWHpcTM3QogjZ8qy
输出:
Program Id: BvRvgPkfWS2bhaD8MXfJWS7rejUyfWHpcTM3QogjZ8qy
Owner: BPFLoaderUpgradeab1e11111111111111111111111
ProgramData Address: 4p3BeSdmZdRqMvsbWJH67befqTy6vmeds1YPJgpakYu6
Authority: 5NhLjdFKocoRMqic9sqAe5TxLagJCoCBunzg51ioMYot
Last Deployed In Slot: 5935
Data Length: 180392 (0x2c0a8) bytes
Balance: 1.2567324 SOL
Authority 与部署钱包一致,存储在 ProgramData Address 的 data 中。
运行:
solana account 4p3BeSdmZdRqMvsbWJH67befqTy6vmeds1YPJgpakYu6 > tempfile
head -n 10 tempfile
输出:
Public Key: 4p3BeSdmZdRqMvsbWJH67befqTy6vmeds1YPJgpakYu6
Balance: 1.2567324 SOL
Owner: BPFLoaderUpgradeab1e11111111111111111111111
Executable: false
Rent Epoch: 18446744073709551615
Length: 180437 (0x2c0d5) bytes
0000: 03 00 00 00 2f 17 00 00 00 00 00 00 01 40 fd 8e ..../........@..
0010: bc ea 6f bd 12 6b dd 2b e5 98 24 ef e9 28 51 6b ..o..k.+..$..(Qk
0020: 28 1e e3 4e d6 c2 9e 2a 83 ca 12 ab 9b 7f 45 4c (..N...*......EL
钱包(5NhL…MYot)的十六进制形式(40fd8e...ab9b)出现在数据中,确认 Authority 的存储位置。
程序账户(Executable: true)不直接存储字节码,而是将其放入 ProgramData Address。这种分离设计提高了模块化,字节码账户仅作为数据容器。
在 Solana 中,只有程序的 owner 才有权限修改其数据。Solana 程序的 owner 默认是 BPFLoaderUpgradeable 系统程序,因此部署程序的钱包在默认情况下无法直接更改存储在账户中的数据(即字节码)。 为了实现程序的可升级性,Solana 运行时会在程序的字节码中嵌入部署者的钱包地址,并将其称为“authority”(权限持有者)。当部署者钱包尝试升级字节码时,Solana 运行时会验证交易的签名者是否与该 authority 相匹配。如果签名者与 authority 一致,BPFLoaderUpgradeable 便会代表 authority 更新程序的字节码。
以下 Python 代码将 Base58 地址转为十六进制:
def decode_base58(bc, length):
base58_digits = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
n = 0
for char in bc:
n = n * 58 + base58_digits.index(char)
return n.to_bytes(length, 'big')
def find_correct_length_for_decoding(base58_string):
for length in range(25, 50):
try:
decoded_bytes = decode_base58(base58_string, length)
return decoded_bytes.hex()
except OverflowError:
continue
return None
base58_string = "5NhLjdFKocoRMqic9sqAe5TxLagJCoCBunzg51ioMYot"
hex_string = find_correct_length_for_decoding(base58_string)
print(hex_string)
【笔记配套代码】 https://github.com/0xE1337/rareskills_evm_to_solana 【参考资料】 https://learnblockchain.cn/column/119 https://www.rareskills.io/solana-tutorial
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!