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/httphttps://solana.com/docs/rpc/websocket

1.5. Devnet Faucet

Solana Devnet 的测试币可通过 RPC requestAirdrop 获得,或者通过网站 https://faucet.solana.com/ 在线获得。

2. 帐户(Accounts)

Solana 中,Accounts 用于存储数据,类似于操作系统中的文件。每个 Account 都有表 1 所示的属性。

Table 1: Account 的属性
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 所示。

solana_tx.gif

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.

说明:

  1. Solana 中没有采用类似 Ethereum 的 Nonce 机制来防止一个交易被处理多次。而是采用其它的方式:在 Tx 中需要指定 Recent blockhash。如果节点收到一个 Tx,发现 Tx 的 Recent blockhash 太旧了,则会直接拒绝;如果 Recent blockhash 比较新,则查看最近有没有处理相同的 Tx(这要求每个节点需要维护最近区块的所有交易列表)。这样,就可以防止一个旧 Tx 被重复处理。
  2. 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. 手续费

Solana 的交易手续费由 3 部分组成:

  1. 每个签名固定收取 5000 Lamports;
  2. 交易执行时所消耗的计算资源;
  3. 小费(它是可选的)。

注 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 Pathsolana-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:

  1. 内置程序(Native Programs)
  2. 链上程序(On-chain Programs)

这两种类型的 Solana Programs 都运行在 Sealevel Runtime 基础上。Sealevel Runtime 具有并行处理能力,可以更快地处理交易。

5.1. 内置程序(Native Programs)

内置程序(Native Programs)直接部署在 Solana 区块链的节点代码中。

下面是内置程序(Native Programs)的例子:

  1. System Program
  2. BPF Loader Program
  3. Vote program

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

Author: cig01

Created: <2023-12-16 Sat>

Last updated: <2024-03-24 Sun>

Creator: Emacs 27.1 (Org mode 9.4)