写在前面:最近学习了下 Sui 相关的知识,本文是一些学习笔记。文章链接较多,点击阅读原文食用更佳。Sui 系列共有 4 篇,剩余的也在这里。https://lmy375.github.io/
Sui
-
区块链浏览器 -
https://suiexplorer.com/ -
https://suiscan.xyz/ -
https://suivision.xyz/ -
源码 https://github.com/MystenLabs/sui/ -
文档 https://docs.sui.io/guides -
中文文档 https://docs-zh.sui-book.com/ -
Intro to Sui https://sui.io/intro-to-sui -
Sui Course https://suicourse.xyz/
Sui 创新点:
-
Object-centric Design(以对象为中心):数据保存在 objects 中,objects 只有其 owner 可以修改。 -
Programmable Transaction Blocks(可编程交易):可以将一笔交易的输出为另一笔交易的输入。这样在不写智能合约的情况下可以构造出一些特殊的逻辑。区块中一组交易保持原子性。 -
Transaction Processing(交易并行处理):对于互不影响的交易可以并行处理。 -
Horizontal Scalability(横向可扩展):网络层面降低成本 -
Move:使 Move 智能合约,语法类似 rust。
相关小知识:
-
Sui 源自日语词 すい (sui)[1],意义为水。发音[2] -
Gas 代币 SUI,最小粒度 MIST( 1 SUI = 1e9 MIST)。可质押参与验证(APY 3.7%)。 -
Sui 地址为 32 bytes,常用 16 进制表示。 -
Sui 交易上原生支持 sponsor 进行 gas fee 代付。sponsor 和 sender 共同签名发送交易。 -
原生支持多签[3]。
基本概念
账户
可以使用 Metamask Sui Snap[4] 作为 Sui 钱包。
Sui 上地址为 32 bytes。示例:
0x2bf6a3a5ff499df04c6ab3e3b2c0be9879b8613e70dbc2c4481c2324bb0aaba3
可以在 suiexplorer[5] 上查看账户余额等信息。
Sui 支持多种签名算法、还支持多签(BLS12-377, BLS12-381, Ed25519, ECDSA secp256k1)。地址由公钥和标志位一起 BLAKE2b hash 得到[6]。
Object
Sui 使用 Object-centric Design 的设计思路。所有操作均是围绕 Object 展开。Object 中保存着各类链上数据,且有对应的 ownership。在 Object 中存储数据要支付对应的费用。
Sui 区块链浏览器中可以查看每个地址下持有 object 的情况。
比如某账户持有链原生代币 SUI,实际就是该账户下持有若干 SUI objects。如下:
每个 object 代表一定的 SUI balance。在浏览器中查看其中一个 object 如下:
相关字段解释:
-
Object ID:该 object 的唯一标识。使用 32 bytes。与地址长度一致,但彼此不重合。 -
Type:表示 object 的类型,这里为 0x0000…0002::coin::Coin<0x0000…0002::sui::SUI>
。表示是 Coin 类型。注意类型在 Sui 体系中似乎是作为元数据保存的,许多数据对象会有明确的字段来保存他的类型,并非只是一种隐式的概念。 -
Version:表示 object 的版本,每次 object 被修改,该 version 都会增加,但不是每次加 1。 -
Last Transaction Block Digest:最近一次修改该 object 的交易。 -
Owner:该 object 的 owner 地址。
不同 object 中有不同的字段(Fields)。其中 id 是均存在的,表示其唯一标识。
SUI Coin Object 中内部又有一个 BALANCE<SUI>
类型的 object,保存着具体的金额。Object 对象的所有权可以 transfer,将 SUI object 转移则对应于 SUI 转账。
一些复杂的 object 还可能有 dynamic fields,指向其他 object id。
Object 除了属于固定的 owner 外(这个 owner 可以是普通账户、package 或其他 object),也可以是 shared,表示所有人均可以访问和修改;还可以是 immutable 表示所有人均不可修改。
即使 Object 属于 owner,也并不意味着 owner 可以任意修改 Object 中的属性。每个 Object 有自己对应的类型(Move 中定义的 struct),因此只能被定义它的 package 所修改。因此 owner 实际也只能通过 package 中的 public function 对 object 进行有限的数据修改。
交易
这是一笔 SUI 转账交易5xXQkPewG6guhQGVHevkrGmw1AjRq4cXadqLL8uDYyhX[7]。
Sui 支持 Programmable Transaction。一笔交易是多个 call 组成。后一个 call 的输入可以是前一个 call 的输出。从而实现简单的编程逻辑。
对于 SUI 转账交易来说包括两个动作:
-
调用 SplitCoins 方法,参数是 GasCoin (即 SUI)和转账金额,将 sender 的 SUI Coin object 分割成两部分。 -
调用 TransferObjects 方法,参数是前面的输出和 receiver 地址,将创建的新 object 转给 receiver,实现转账。
在 Object Change 一栏中可以看到 object 创建与修改的情况。
可以注意到 object 修改后版本号会增加。另外需要注意的是,一笔交易中的 object 版本号均会更新成相同的值,而非各自更新。
另外交易中设定了 Gas Payment Object
,根据 Gas Object Owner
不同,可以实现 gas 代付机制。
下面看一笔复杂一点的交易,HnA5VD6LmoeWdkVAMYfU7wtydfwfaTxxETBhCGgZtyM1[8] 是一笔 DEX Swap 交易。值得注意的点包括:
-
合约使用调用 MoveCall 方法。本例中调用的方法为 0x9632f61a796fc54952d9151d80b319e066cba5498a27b495c99e113db09726b1::swap_router::swap_a_b
。与以太坊不同的是,合约方法调用除了常规的参数外,还有type_arguments
即类型参数。有点类似 C++ 中的泛型机制。具体可以看 Move 相关的文档。 -
Sui 使用类似以太坊的 event 机制来输出一些交易过程的信息。
交易的 sender 需要有交易中 input 中的 object 的访问权限。即
-
object owner 是 sender -
object 是 shared 的 -
object 是 immutable 的
Package
Package 即类似以太坊的智能合约概念。0x9632f61a796fc54952d9151d80b319e066cba5498a27b495c99e113db09726b1[9] 就是一个 Package。
Package 实际是一种 immutable 的 object。因此可使用 object id 唯一标识。另外由于是 immutable 的,代码部署后即不可以更改。Package 也有 version,开发者发布新的 Package 时 version 加 1,但需要注意此时 object id 也会变化。旧的合约仍存在链上可被调用。需要开发者自行维护 version 检查与更新逻辑。
Move 合约编译成 package bytecode 后可以部署到网络上。开发者根据功能划分又可在 package 中区别为不同的 module。每个 module 可以对外公开 function 允许外部调用。其中 module 和 function 直接使用字符串索引。区块链浏览器上也允许直接对公开的方法进行调用。
由于 Move bytecode 比较简单,大多数区块链浏览器自带反汇编功能,可以直接查看原始 bytecode。另外接口定义似乎也作为元数据保存在链上,不需要像以太坊一样在链下使用 ABI 进行辅助。
Sui 网络上已经部署了一些系统 package 供开发者使用。这些 package 的 Publisher 是 0x00..00
地址。系统 package 包括:
-
std
地址 0x1[10],一些基础类型等。 -
sui
地址 0x2[11],Sui 核心模块,包括 object, transfer, token, tx_context, package 等,几乎所有合约都要使用。 -
sui_system
地址 0x3[12],Staking, Validator 等相关内容。
这些 package 是经过开源验证的,可以在区块链浏览器中查看源码。
这里[13]可以查看近期调用比较多的 package, module, function。
package 升级
package 是可升级[14] 的。但需要保证升级前后兼容。
-
public function 和 struct 定义不变 -
可添加新 function 和 struct -
function 实现可修改 -
non-public(包括 friend 和 entry)定义可修改
升级需要持有 UpgradeCap。升级后 version + 1,package id 改变。
相关命令
# 发布 package
sui client publish --gas-budget <GAS-BUDGET-AMOUNT>
# 查找某个地址是否有升级权限(UpgradeCap)
sui client objects $OWNER | grep UpgradeCap -A 2 -B 4
# 升级 package
sui client upgrade --gas-budget <GAS-BUDGET-AMOUNT> --upgrade-capability <UPGRADE-CAP-ID>
这是一个例子:Package: Turbos Finance 2 0xeb92..ee6d[15] 升级为 Package: Turbos Finance 3 0x9632..26b1[16] 的交易[17]
-
由持有 0x2::package::UpgradeCap[18] 的 Owner[19] 发起交易 -
交易内容为 -
MoveCall sui::package::authorize_upgrade
,由 UpgradeCap 获得 UpgradeTicket -
Upgrade 0xeb92..ee6d
使用 UpgradeTicket 创建新的 Package object,并返回 UpgradeReceipt -
MoveCall sui::package::commit_upgrade
使用 UpgradeReceipt 更新 UpgradeCap。
更新完成后 0xeb92..ee6d
(Version 7)中 bytecode 中所引用的 package id 将变为 0x9632..26b1
(Version 8)。这一点在区块链浏览器中可以看到。如 Version 7 更新后的 bytecode 反汇编代码如下:
// Move bytecode v6
module 91bfbc386a41afcfd9b2533058d7e915a1d3829089cc268ff4333d54d6339ca1.swap_router {
use 0000000000000000000000000000000000000000000000000000000000000002::clock;
use 0000000000000000000000000000000000000000000000000000000000000002::coin;
use 0000000000000000000000000000000000000000000000000000000000000002::tx_context;
use 91bfbc386a41afcfd9b2533058d7e915a1d3829089cc268ff4333d54d6339ca1::pool;
...
所有被更新过的 package,其 package 引用会与自身 package id 不同。以此可以用来辨别 package 是否有更新版本。
新旧使用相同的 package 引用,就使得新旧版本可以处理同样的 object。这样就保证了新版本 object 对旧版本的兼容。
package 中的代码是不可修改的,其中具体的 bytecode 仍保持旧版不变。因此更新新版本并不会使旧版本失效。package 内部可以通过特殊的代码逻辑实现此功能。典型的一种实现方式如下:
struct Versioned has key, store {
id: UID,
version: u64,
}
const VERSION: u64 = 1;
public fun foo(versioned: &Versioned) {
assert!(versioned.version == VERSION, EWrongVersion);
// ...
}
定义一个 object 保存当前版本号。并在代码中检查 object 中的版本与代码中的常量版本号是否一致。当 package 更新后,管理员可以将 Versioned object 中的 version 更新为 2。此时旧版 package 中的 foo 函数运行将报 EWrongVersion 错误。注意,这种方案要求每个函数都额外增加一个 Versioned 参数,并在函数执行前进行版本检查。
命令行
参考:https://docs.sui.io/references/cli
安装
# 使用 homebrew
brew tap mystenlabs/tap
brew install mystenlabs/tap/sui
# 或直接从源码安装
cargo install --locked --git https://github.com/MystenLabs/sui.git --branch mainnet sui
使用
# 切换网络
sui client new-env --alias mainnet --rpc https://fullnode.mainnet.sui.io:443
sui client switch --env mainnet
# 查看地址的 objects
sui client objects $ADDR
# 查看某个 object
sui client object $OBJECT
# 进行方法调用
sui client call --gas-budget <GAS-AMOUNT> --package $PACKAGE --module $MODULE --function $FUNC --args $ARG1 $ARG2
Move 相关
# 创建 move project
sui move new $PACKAGE
RPC
RPC 文档见 https://docs.sui.io/sui-api-ref
最关键的发送交易 RPC 方法为 sui_executeTransactionBlock[20]
请求示例为
{
"jsonrpc": "2.0",
"id": 1,
"method": "sui_executeTransactionBlock",
"params": [
// 交易的 binary data,使用 Base64 编码
"AAACACBqEB6aOvXIBwES+Ahkizbvv43uihqC3kbZUE6WoRCKFwEAjvdvVsOZYzousxC8qRJOXy84znOeqsu2YAaIgE4HhEgCAAAAAAAAACB9w3+ufZMpihJFwxtCBojBaGy00TVtFxgN2C6TpIPFqwEBAQEBAAEAAAS0l6kWtGVmCaf6gnoJGE1vR2gdO6dM4NejbGSysfiHAZ+Q9/hmzCnfsdpjc86U+dldylpA9OF2mRjuv5+64AvTAgAAAAAAAAAgjleHL0UiRGjh/BfIFHCJ3EMY/dQA22c2TvNQyVJnbYUEtJepFrRlZgmn+oJ6CRhNb0doHTunTODXo2xksrH4hwoAAAAAAAAAoIYBAAAAAAAA",
[
// 签名,可能有多个,使用 Base64 编码
"AKD4XdltkCyBi1Heb4EJJ3lzuV3F4u7+CYeaE+Fd7qXpaT17yd4tHWjMf4CWq3TuXBLxTpkc2MV39P6p7eMV8QnqvbuA0Q1Bqu4RHV3JPpqmH+C527hWJGUBOZN1j9sg8w=="
],
{
"showInput": true,
"showRawInput": true,
"showEffects": true,
"showEvents": true,
"showObjectChanges": true,
"showBalanceChanges": true
},
"WaitForLocalExecution"
]
}
交易 binary data 使用 Binary Canonical Serialization, BCS[21] 进行序列化。
参考资料
寫作「水」讀作「すい」的日語詞: https://zh.wiktionary.org/wiki/Category:%E5%AF%AB%E4%BD%9C%E3%80%8C%E6%B0%B4%E3%80%8D%E8%AE%80%E4%BD%9C%E3%80%8C%E3%81%99%E3%81%84%E3%80%8D%E7%9A%84%E6%97%A5%E8%AA%9E%E8%A9%9E
[2]sui 的发音: https://zh.forvo.com/word/%E6%B0%B4_mizu%2C_sui/#ja
[3]Sui 的多签支持: https://docs.sui.io/concepts/cryptography/transaction-auth/multisig
[4]Metamask Sui Snap: https://snaps.metamask.io/snap/npm/kunalabs-io/sui-metamask-snap/
[5]Sui Block Explorer: https://suiexplorer.com/address/0x2bf6a3a5ff499df04c6ab3e3b2c0be9879b8613e70dbc2c4481c2324bb0aaba3?network=mainnet
[6]Sui 地址格式: https://docs.sui.io/concepts/cryptography/transaction-auth/keys-addresses#address-format
[7]Sui 转账交易示例: https://suiscan.xyz/mainnet/tx/5xXQkPewG6guhQGVHevkrGmw1AjRq4cXadqLL8uDYyhX
[8]DEX 交易示例: https://suiscan.xyz/mainnet/tx/HnA5VD6LmoeWdkVAMYfU7wtydfwfaTxxETBhCGgZtyM1
[9]Package 示例: https://suiexplorer.com/object/0x9632f61a796fc54952d9151d80b319e066cba5498a27b495c99e113db09726b1?network=mainnet
[10]Sui std(0x1) 地址: https://suiexplorer.com/object/0x0000000000000000000000000000000000000000000000000000000000000001?network=mainnet
[11]Sui sui(0x2) 地址: https://suiexplorer.com/object/0x0000000000000000000000000000000000000000000000000000000000000002?network=mainnet
[12]Sui system(0x3) 地址: https://suiexplorer.com/object/0x0000000000000000000000000000000000000000000000000000000000000003?network=mainnet
[13]suivision packages 列表: https://suivision.xyz/packages
[14]Sui package 升级: https://docs.sui.io/concepts/sui-move-concepts/packages/upgrade
[15]Package: Turbos Finance 2 0xeb92..ee6d: https://suiscan.xyz/mainnet/object/0xeb9210e2980489154cc3c293432b9a1b1300edd0d580fe2269dd9cda34baee6d
[16]Package: Turbos Finance 3 0x9632..26b1: https://suiscan.xyz/mainnet/object/0x9632f61a796fc54952d9151d80b319e066cba5498a27b495c99e113db09726b1
[17]Package 升级交易示例: https://suivision.xyz/txblock/71RiMs79kj9ruCeo6D1QN8h4tAGiZWXPsj8QfKiACRSa
[18]0x2::package::UpgradeCap: https://suiscan.xyz/mainnet/object/0x00a8e96385ed222c04fab9a95c9e12307e2a7b4375551338289101e78d193eee
[19]UpgradeCap 的 Owner: https://suiscan.xyz/mainnet/account/0x0a475e3bd09b7e38ef8e200dcf81b55630f7f8c93f4465005316002184051ea2
[20]Sui API: sui_executeTransactionBlock: https://docs.sui.io/sui-api-ref#sui_executetransactionblock
[21]Binary Canonical Serialization: https://github.com/diem/bcs
原文始发于微信公众号(Diary of Owen):Sui 实用学习笔记