Solana (Blockchain Platform)
Table of Contents
1. Solana
Solana 是一个高性能的区块链平台。Solana 这个名字来源于它的创建者 Anatoly, Greg and Stephen 在圣地亚哥(San Diego)为高通工作时一起冲浪的“一个海滩的名称”。
Solana 采用是的 Proof of Stake(PoS)机制。矿工把原生代币 SOL 抵押给验证节点(Validators),Validators 之间会选择一个 Leader 进行出块,同一时刻只有一个 Leader,每隔 4 Slots(即 Blocks)会轮换 Leader。
Solana 中提出了 Proof of History(PoH),它并不是一个共识机制,可以认为 PoH 实现了分布式系统的“全局时钟”。 有了 PoH,Solana 大大减少了 PBFT 协议中消息的传递,这个改版的 PBFT 被称为 Tower BFT。
When used alongside a consensus algorithm such as Proof of Work (PoW) or Proof of Stake (PoS), PoH can reduce messaging overhead in a Byzantine Fault Tolerant replicated state machine, resulting inn sub-second finality times.
摘自 Solana 白皮书:Solana: A new architecture for a high performance blockchain v0.8.13
1.1. Epoch, Slot
Solana 中有个 Epoch 的概念。 The lifetime of a leader schedule is called an epoch. 也就是说,在一个 Epoch 内,Leader Schedule 不会变。Leader Schedule 就是即轮流当 Leader 的方案,比如某个 Epoch 内确定了由验证节点 A,B,C 轮流当 Leader,那么这个 Epoch 内这个方案就不再改变了。假设有新 Validator 加入,那么在后续的 Epoch 中才可能成为 Leader。
Epoch 由 432,000 个 Slots(即 Blocks)组成, 目前一个 Slot 约 400 ms,也就是说一个 Epoch 约 2-3 天。
1.2. Lamport
Solana 中原生币被称为 SOL,而原生币的最小单位为 Lamport,它们之间的关系是:1 SOL = 1000000000 Lamports(数字 1 后面有 9 个零)。
1.3. Ed25519
Solana 中签名算法为 Ed25519,而 帐户地址就是 Ed25519 公钥的 base58 编码。
1.4. RPC 方法
Solana 节点所支持的 RPC 方法可参考 https://solana.com/docs/rpc/http 和 https://solana.com/docs/rpc/websocket 。
1.5. Devnet Faucet
Solana Devnet 的测试币可通过 RPC requestAirdrop 获得,或者通过网站 https://faucet.solana.com/ 在线获得。
2. 帐户(Accounts)
Solana 中,Accounts 用于存储数据,类似于操作系统中的文件。每个 Account 都有表 1 所示的属性。
Field | Description |
---|---|
lamports | The number of lamports owned by this account. 账户余额 |
owner | The program owner of this account. 账户的 program owner |
executable | Whether this account can process instructions. 帐户是否可执行 |
data | The raw data byte array stored by this account. 帐户关联的数据 |
rent_epoch | The next epoch that this account will owe rent |
如果 Account 上存储了 data,则需要为其支付租金(rent)。如果 Account 的余额足够支付 2 年的租金,则会免租(Rent exempt)。
通过 RPC getAccountInfo 可以查看一个帐户的相关信息,如:
$ curl https://api.devnet.solana.com -X POST -H "Content-Type: application/json" -d ' { "jsonrpc": "2.0", "id": 1, "method": "getAccountInfo", "params": [ "AT42Zu5kD6V27rgXdkoTpE4mqvsDxsitzWYDr1iStu3r", { "encoding": "base58" } ] } ' { "jsonrpc": "2.0", "result": { "context": { "apiVersion": "1.17.6", "slot": 266163556 }, "value": { "data": [ "", "base58" ], "executable": false, "lamports": 2222036560, "owner": "11111111111111111111111111111111", "rentEpoch": 18446744073709551615, "space": 0 } }, "id": 1 }
参考:
https://solanacookbook.com/core-concepts/accounts.html
https://solana.wiki/docs/solidity-guide/accounts/
3. 交易
Solana 中,Tx 的格式如图 1 所示。
Figure 1: Solana Tx(摘自:https://solana.com/docs/intro/dev#transactions )
其字段的说明如下:
1、Signatures: An array of digital signatures from the transaction's signers.
2、Message: The actual instructions that the transaction is issuing to the network.
2.1、Message header: 3 uint8s describing how many accounts will sign the payload, how many won’t, and how many are read-only.
2.2、Account addresses: an array of addresses of the accounts that will be used in the transaction.
2.3、Recent blockhash: a unique value that identifies a recent block - this ensures the transaction is not too old and is not re-processed.
2.4、Instructions: which program to call, which accounts to use, and any additional data needed for the program to execute the instruction.
说明:
- Solana 中没有采用类似 Ethereum 的 Nonce 机制来防止一个交易被处理多次。而是采用其它的方式:在 Tx 中需要指定 Recent blockhash。如果节点收到一个 Tx,发现 Tx 的 Recent blockhash 太旧了,则会直接拒绝;如果 Recent blockhash 比较新,则查看最近有没有处理相同的 Tx(这要求每个节点需要维护最近区块的所有交易列表)。这样,就可以防止一个旧 Tx 被重复处理。
- Fee 并没有体现在 Tx 中。对于普通转账,目前策略是对每个签名收取 5000 Lamports。并且由 Account addresses 中的首个地址支付 Fee。当然也可以指定小费加快交易,参考节 3.1。
参考:
https://solana.com/docs/core/transactions
https://solana.wiki/docs/solidity-guide/transactions/
https://solanacookbook.com/core-concepts/transactions.html
3.1. 手续费
- 每个签名固定收取 5000 Lamports;
- 交易执行时所消耗的计算资源;
- 小费(它是可选的)。
注 1:关于第 1 部分和第 2 部分的手续费可以通过 RPC getFeeForMessage 得知,这两部分是自动扣取的,并不需要在提交交易时指定。
注 2:如果想在交易中设置小费来加快交易的打包上链过程,则可以在交易中使用 Compute Budget 指令,它主要有两个参数,即 SetComputeUnitPrice 和 SetComputeUnitLimit。例如,交易 23cHBWBcgfBYtA4Zimyky9wxdEeoZgqWqewQAyHQYa4EbRKh7b85CrifGZ4wshRr9jr3U2kfr1a5QNN5xhf4XbPV 使用了 Compute Budget 指令设置小费来加快打包上链。
参考:https://solana.com/docs/intro/transaction_fees#calculating-transaction-fees
4. 钱包基础操作(命令行演示)
下面演示一下如何通过 Solana Command Line 进行钱包的基本操作,如创建、转账、查看余额等。
4.1. 创建钱包
使用 solana-keygen new
可以创建钱包。创建第 1 个钱包:
$ solana-keygen new Generating a new keypair For added security, enter a BIP39 passphrase NOTE! This passphrase improves security of the recovery seed phrase NOT the keypair file itself, which is stored as insecure plain text BIP39 Passphrase (empty for none): Wrote new keypair to /Users/user1/.config/solana/id.json ========================================================================= pubkey: AT42Zu5kD6V27rgXdkoTpE4mqvsDxsitzWYDr1iStu3r # Solana 中,钱包地址就是 Ed25519 公钥的 base58 编码 ========================================================================= Save this seed phrase and your BIP39 passphrase to recover your new keypair: click amateur flip company best wet bar unhappy child jealous when nephew # 需要保密的 12 个助记词 =========================================================================
我们可以看看 keypair 文件的具体内容:
$ cat /Users/user1/.config/solana/id.json [170,94,113,29,19,58,202,69,214,236,151,17,109,229,74,36,164,33,168,35,56,95,196,47,211,66,213,236,177,124,231,208,140,101,180,190,206,4,237,141,9,174,84,82,252,86,223,173,233,148,105,192,129,85,20,137,151,117,134,90,247,120,63,157]
这个 json 文件的内容是字节数组(64 个元素),它其实就是 ed25519 的私钥和公钥(字节数组的前 32 字节是私钥,后 32 字节是公钥),参考:https://mattmazur.com/2021/11/19/splitting-a-solana-keypair-into-a-public-and-private-keys/ ,相关源码在 https://github.com/solana-labs/solana/blob/550ca7bf92db57d309fa91479b414786da968fa7/sdk/src/signer/keypair.rs#L119
通过 faucet,往第 1 个钱包转入 1 个 SOL 测试币:
$ solana airdrop 1 AT42Zu5kD6V27rgXdkoTpE4mqvsDxsitzWYDr1iStu3r --url https://api.devnet.solana.com Requesting airdrop of 1 SOL Signature: rCioafR3y873N5X6gritMhAJSFJdLVXR1EV6LJHLqCzMdP5A8tqANM9kF6Y3ubTwMyZd5XcasEcZnU3STbXECUy 1 SOL
上面命令输出的 Signature 后的字段就是 tx,在区块浏览器上可以查看它的信息,如:https://solscan.io/tx/rCioafR3y873N5X6gritMhAJSFJdLVXR1EV6LJHLqCzMdP5A8tqANM9kF6Y3ubTwMyZd5XcasEcZnU3STbXECUy?cluster=devnet
4.2. 推导地址
4.2.1. 从 keypair json 文件推导地址
使用 solana-keygen pubkey [KEYPAIR]
可以显示 keypair 文件所对应的地址(即公钥):
$ solana-keygen pubkey /Users/user1/.config/solana/id.json AT42Zu5kD6V27rgXdkoTpE4mqvsDxsitzWYDr1iStu3r $ solana-keygen pubkey AT42Zu5kD6V27rgXdkoTpE4mqvsDxsitzWYDr1iStu3r
4.2.2. 从助记词推导地址
使用 solana-keygen pubkey ASK
可以从用户输入的助记词推导出地址(即公钥),如:
$ solana-keygen pubkey ASK [pubkey recovery] seed phrase: <= 这里输入助记词,如 click amateur flip company best wet bar unhappy child jealous when nephew [pubkey recovery] If this seed phrase has an associated passphrase, enter it now. Otherwise, press ENTER to continue: AT42Zu5kD6V27rgXdkoTpE4mqvsDxsitzWYDr1iStu3r
上面命令推导出的公钥是 bip32 中 Master Key 所对应的公钥,并不涉及 Derivation Path。
需要说明的是,其它钱包一般不会直接使用 bip32 的 Master Key,而是会使用一个 Derivation Path 得到子私钥,再得到它的公钥。比如 Trust Wallet 使用 m/44'/501'/0'
作为 Solana 的 Derivation Path。 solana-keygen pubkey
支持定制 Derivation Path 来推导子私钥,如:
$ solana-keygen pubkey "prompt://?full-path=m/44'/501'/0'" [pubkey recovery] seed phrase: <= 这里输入助记词,如 click amateur flip company best wet bar unhappy child jealous when nephew [pubkey recovery] If this seed phrase has an associated passphrase, enter it now. Otherwise, press ENTER to continue: 6hSCKjhkkcjqXFDQCyc2MkUHKcKTJMehoxzcqVauJGoj
参考:https://forums.solana.com/t/paperwallet-addresses-bad-guide-in-solana-docs/3042
4.3. 测试转账
在测试转账前,我们先创建第 2 个钱包,以作为接收地址:
$ solana-keygen new --outfile my_solana_wallet.json Generating a new keypair For added security, enter a BIP39 passphrase NOTE! This passphrase improves security of the recovery seed phrase NOT the keypair file itself, which is stored as insecure plain text BIP39 Passphrase (empty for none): Wrote new keypair to my_solana_wallet.json ============================================================================= pubkey: TkPgkcDQYDUSDBfwTokwkZpn8spz94u4fvXii9U7CWY ============================================================================= Save this seed phrase and your BIP39 passphrase to recover your new keypair: witness magnet rail absent modify harsh name coin endorse stem position green =============================================================================
使用 solana transfer
可以往另一个地址转账,如:
$ solana transfer TkPgkcDQYDUSDBfwTokwkZpn8spz94u4fvXii9U7CWY 0.1 --allow-unfunded-recipient --url https://api.devnet.solana.com Signature: 2Z3a9hLA1rprRpUbwvPWVNtKytr9k8y7PzHunxoJG25Qes6pXaubarshG2fJj5bwGndYudnTT4U3H8UMVxYhx3ec
注:当往一个新地址转账时,需要指定 --allow-unfunded-recipient
参数。
可在区块浏览器中查看上面的转账:https://solscan.io/tx/2Z3a9hLA1rprRpUbwvPWVNtKytr9k8y7PzHunxoJG25Qes6pXaubarshG2fJj5bwGndYudnTT4U3H8UMVxYhx3ec?cluster=devnet
4.4. 查看余额
通过 RPC getBalance 可以查看余额。在命令行中,使用 solana balance
可以查看某帐户的余额,如:
$ solana balance TkPgkcDQYDUSDBfwTokwkZpn8spz94u4fvXii9U7CWY --url https://api.devnet.solana.com 0.1 SOL $ solana balance TkPgkcDQYDUSDBfwTokwkZpn8spz94u4fvXii9U7CWY --lamports --url https://api.devnet.solana.com 100000000 lamports
5. 智能合约(Solana Programs)
本质上说, Solana Programs(智能合约)就是 executable 属性为 true 的 Account。
有两种类型的 Solana Programs:
- 内置程序(Native Programs)
- 链上程序(On-chain Programs)
这两种类型的 Solana Programs 都运行在 Sealevel Runtime 基础上。Sealevel Runtime 具有并行处理能力,可以更快地处理交易。
5.1. 内置程序(Native Programs)
内置程序(Native Programs)直接部署在 Solana 区块链的节点代码中。
下面是内置程序(Native Programs)的例子:
5.2. 链上程序(On-chain Programs)
目前 Solana 支持 Rust 和 C/C++ 编写链上程序,它们会被 LLVM 编译为 BPF 字节码提交到链上执行。
参考:https://solana.com/developers/guides/getstarted/hello-world-in-your-browser
6. 参考
Solana Documentation: https://solana.com/docs
Guide for Solidity Developers: https://solana.wiki/docs/solidity-guide/
Solana Cookbook: https://solanacookbook.com
Solana Whitepaper: Solana: A new architecture for a high performance blockchain v0.8.13
Solana Github: https://github.com/solana-labs
Solana RPC Documentation: https://solana.com/docs/rpc
Solana Proof of Stake + Proof of History Primer: https://www.shinobi-systems.com/primer.html