Contents

Sui 实用学习笔记 (1) - 概述

   Jan 19, 2024     4 min read      views

Sui 基本知识:address, object, package。命令行与 package。

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),意义为水。发音
  • Gas 代币 SUI,最小粒度 MIST( 1 SUI = 1e9 MIST)。可质押参与验证(APY 3.7%)。
  • Sui 地址为 32 bytes,常用 16 进制表示。
  • Sui 交易上原生支持 sponsor 进行 gas fee 代付。sponsor 和 sender 共同签名发送交易。
  • 原生支持多签

基本概念

账户

可以使用 Metamask Sui Snap 作为 Sui 钱包。

Sui 上地址为 32 bytes。示例:

0x2bf6a3a5ff499df04c6ab3e3b2c0be9879b8613e70dbc2c4481c2324bb0aaba3

可以在 suiexplorer 上查看账户余额等信息。

Sui 支持多种签名算法、还支持多签(BLS12-377, BLS12-381, Ed25519, ECDSA secp256k1)。地址由公钥和标志位一起 BLAKE2b hash 得到

Object

Sui 使用 Object-centric Design 的设计思路。所有操作均是围绕 Object 展开。Object 中保存着各类链上数据,且有对应的 ownership。在 Object 中存储数据要支付对应的费用。

Sui 区块链浏览器中可以查看每个地址下持有 object 的情况。

比如某账户持有链原生代币 SUI,实际就是该账户下持有若干 SUI objects。如下:

image

每个 object 代表一定的 SUI balance。在浏览器中查看其中一个 object 如下:

image

相关字段解释:

  • 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

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 是一笔 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 就是一个 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,一些基础类型等。
  • sui 地址 0x2,Sui 核心模块,包括 object, transfer, token, tx_context, package 等,几乎所有合约都要使用。
  • sui_system 地址 0x3,Staking, Validator 等相关内容。

这些 package 是经过开源验证的,可以在区块链浏览器中查看源码。

这里可以查看近期调用比较多的 package, module, function。

package 升级

package 是可升级 的。但需要保证升级前后兼容。

  • 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 升级为 Package: Turbos Finance 3 0x9632..26b1交易

  • 由持有 0x2::package::UpgradeCapOwner 发起交易
  • 交易内容为
    • 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

请求示例为

{
  "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 进行序列化。