当前位置: 首页 > news >正文

IOTA 学习笔记(十一):共享对象与多用户交互

上一期我们讲了 IOTA 中的交易与 PTB,也就是可编程交易块。PTB 的重点在于:一笔交易不一定只做一次转账或一次函数调用,而是可以把对象拆分、对象转移、Move 函数调用等多个步骤组合起来,形成一次原子执行。

这一期继续往对象模型深入,讲一个非常重要的概念:共享对象,也就是 Shared Object。

在前面的 Counter 合约中,我们创建的是一个归属于某个地址的对象。这个对象有明确的 owner,通常只有对象所有者可以以可变方式操作它。这类对象比较容易理解。但在真实应用中,很多链上状态并不只属于某一个用户,而是需要被多个用户共同访问。例如市场、投票系统、公共计数器、游戏房间、流动性池、注册表等。

这些场景就需要共享对象。

简单来说:

Owned Object 适合表达个人资产或个人状态,Shared Object 适合表达多个用户共同访问的链上状态。

1. 先回顾 Owned Object

Owned Object 是归某个地址所有的对象。

例如,上一期 Counter 示例中,用户调用 create 函数后,会得到一个 Counter 对象:

Address A owns Counter Object Counter.value = 0

之后,Address A 可以调用 increment 函数修改这个 Counter:

Address A 调用 increment Counter.value = 1

这个过程比较简单,因为 Counter 明确属于 Address A。其他地址通常不能直接以可变方式修改它。

Owned Object 的特点可以总结为:

有明确所有者 通常只能由所有者操作 适合个人状态或个人资产 交易处理路径相对简单

例如:

用户自己的 Coin 用户自己的 NFT 用户自己的 Counter 用户自己的凭证对象 用户自己的配置对象

这些都比较适合作为 Owned Object。

2. 为什么需要 Shared Object

但是,并不是所有链上状态都适合归某一个用户所有。

假设我们要写一个公共计数器。任何用户都可以调用 increment,使计数器加 1。如果这个计数器只归 Address A 所有,那么 Address B、Address C 就不能自然地修改它。

再比如,一个链上商店 Shop。所有用户都可以购买商品。如果 Shop 对象只归店主所有,那么普通用户调用购买函数时,就会遇到对象访问问题。

类似场景很多:

公共计数器 链上商店 投票系统 订单市场 流动性池 多人游戏状态 公共注册表 共享配置对象

这些对象的共同特点是:

不是某一个用户私有 多个用户都需要访问 状态可能被不同交易持续修改

这时就需要 Shared Object。

Shared Object 可以被多个用户访问。也就是说,它不再只属于某一个地址,而是作为全网可访问的共享状态存在。

可以简单理解为:

Owned Object: Address A owns Object X Shared Object: Object X is shared and can be accessed by multiple users

3. Shared Object 的核心特点

Shared Object 有几个重要特点。

第一,它可以被多个用户访问。
这是共享对象和地址拥有对象最直接的区别。

第二,它仍然是链上对象。
共享对象也有 Object ID、类型、版本和数据,不是普通全局变量。

第三,它通常需要keyability。
因为它要作为链上对象被识别和操作。

第四,它一旦共享,通常就不再回到普通 owned object 的模式。
也就是说,共享是一个非常重要的状态变化,设计时要谨慎。

第五,涉及共享对象的交易通常需要共识排序。
因为多个用户可能同时操作同一个共享对象,系统必须确定交易顺序。

这几个特点决定了 Shared Object 既强大,也更复杂。

可以概括为:

Shared Object = 可被多个用户访问的链上对象

但还要补一句:

共享不等于没有规则 共享对象的具体访问权限仍然由 Move 函数控制

这是初学者很容易混淆的地方。

4. 共享不等于任何人都能随便修改

很多人看到 shared,就会以为“所有人都能随便改”。这个理解不准确。

Shared Object 的意思是:这个对象可以作为多个用户交易中的输入对象。但用户能不能成功修改它,还要看 Move 函数怎么写。

例如,一个共享商店对象 Shop 可以允许所有用户购买商品:

buy(shop: &mut Shop, payment: Coin<IOTA>)

但它可以只允许店主提取收益:

collect_profits(shop: &mut Shop, cap: &ShopOwnerCap)

这里的ShopOwnerCap就是一种权限对象。只有持有这个权限对象的人,才能调用提取收益函数。

所以,Shared Object 的正确理解是:

对象可以被多个用户访问 但具体操作仍然受函数参数、权限对象和合约逻辑约束

这和现实世界也很像。

一个商店是公共的,所有人都可以进店买东西。
但只有店主可以收银、改价格、取走利润。

因此,写共享对象合约时,权限设计非常重要。

5. 如何创建 Shared Object

在 Move 中,创建共享对象通常分成两步:

先创建对象 再把对象 share 出去

以一个共享计数器为例,可以写成类似这样:

module hello::shared_counter { use iota::object::{Self, UID}; use iota::transfer; use iota::tx_context::TxContext; public struct SharedCounter has key { id: UID, value: u64, } fun init(ctx: &mut TxContext) { let counter = SharedCounter { id: object::new(ctx), value: 0, }; transfer::share_object(counter); } public entry fun increment(counter: &mut SharedCounter) { counter.value = counter.value + 1; } public fun value(counter: &SharedCounter): u64 { counter.value } }

这段代码的核心是:

transfer::share_object(counter);

它把新创建的 counter 对象变成共享对象。

和普通 transfer 不同,普通对象创建后通常会转移给某个地址:

transfer::transfer(counter, sender);

而共享对象创建后,会被放到全网可访问的共享状态中:

transfer::share_object(counter);

可以这样对比:

transfer::transfer: 把对象转移给某个地址 transfer::share_object: 把对象变成共享对象

这就是 Owned Object 和 Shared Object 在创建方式上的关键区别。

6. 共享计数器示例怎么理解

上面的 SharedCounter 合约可以帮助我们理解共享对象的基本流程。

合约发布后,init函数会创建一个 SharedCounter 对象,并把它共享出去。

初始状态可以理解为:

SharedCounter Object value = 0 owner = shared

之后,用户 A 可以调用:

increment(shared_counter)

执行后:

value = 1

用户 B 也可以调用:

increment(shared_counter)

执行后:

value = 2

用户 C 再调用:

increment(shared_counter)

执行后:

value = 3

这个过程中,SharedCounter 不属于用户 A,也不属于用户 B,而是一个共享对象。多个用户都可以把它作为参数传入交易。

这就是共享对象的基本意义。

7. 为什么共享对象需要共识排序

Shared Object 最大的复杂性来自并发。

假设 SharedCounter 当前值是 0。现在用户 A 和用户 B 几乎同时提交交易:

用户 A:increment(counter) 用户 B:increment(counter)

这两笔交易都想修改同一个共享对象。

如果没有统一排序,不同节点可能得到不同结果。

例如,节点 1 认为顺序是:

A 先执行,B 后执行

节点 2 认为顺序是:

B 先执行,A 后执行

对于简单加 1 来说,最终值可能都是 2,看起来影响不大。但对于复杂应用,顺序可能非常关键。

例如:

第一个买家买走唯一商品 第一个出价者获得拍卖资格 第一个提交者占用某个名称 第一个交易改变价格 第一个交易清空库存

这些场景中,交易顺序会直接影响结果。

因此,涉及共享对象的交易需要进入共识排序。网络必须先确定这些交易的全局顺序,再让验证者按照相同顺序执行。这样,所有节点才能得到一致状态。

可以简单理解为:

Owned Object: 只涉及某个所有者的对象,处理路径较快 Shared Object: 多个用户可能同时修改,需要共识确定顺序

这就是共享对象比 owned object 更复杂的根本原因。

8. Owned Object 和 Shared Object 的交易路径差异

可以用一个简单对比理解两类对象的交易路径。

对于 Owned Object:

用户提交交易 验证对象所有权 锁定对象版本 执行交易 更新对象版本

如果交易只涉及 owned objects,系统可以更快处理,因为对象有明确 owner,不需要全网对所有并发访问进行统一排序。

对于 Shared Object:

用户提交交易 识别共享对象输入 进入共识排序 确定交易顺序 按顺序执行交易 更新共享对象版本

区别的核心在于:

Owned Object 重点检查所有权 Shared Object 重点处理并发顺序

所以,如果一个应用不需要多人共同修改同一个对象,就不应该轻易使用 Shared Object。共享对象功能强大,但也会带来更高的复杂度和执行成本。

9. 对象版本在共享对象中怎么变化

共享对象也有版本。

每次共享对象被交易修改后,都会产生新的版本。

例如,SharedCounter 初始版本是 1:

Version 1 value = 0

用户 A 调用 increment 后:

Version 2 value = 1

用户 B 调用 increment 后:

Version 3 value = 2

用户 C 调用 increment 后:

Version 4 value = 3

看起来和 owned object 类似,但共享对象版本的确定更依赖交易调度和共识排序。

因为多个用户可能同时提交交易,系统必须先确定顺序,再决定每笔交易读写的是哪个状态版本。

可以简单理解为:

共享对象的版本变化 = 共识排序后的状态演进结果

这也是为什么查询共享对象时,版本信息很重要。它能反映这个共享对象经历了多少次状态更新。

10. 共享对象和权限对象

共享对象经常和权限对象配合使用。

因为共享对象可以被多个用户访问,但并不是所有操作都应该开放给所有人。

例如,一个 DonutShop 共享对象可以允许任何用户购买甜甜圈,但只有店主可以提取收益。

可以设计两个对象:

DonutShop:共享对象 ShopOwnerCap:店主权限对象

购买函数:

public entry fun buy(shop: &mut DonutShop, payment: Coin<IOTA>) { // 所有人都可以调用 }

提取收益函数:

public entry fun collect(shop: &mut DonutShop, cap: &ShopOwnerCap) { // 只有持有 ShopOwnerCap 的人才能调用 }

这里的ShopOwnerCap就像一把钥匙。

共享对象负责承载公共状态。
权限对象负责限制敏感操作。

这种设计非常常见。

可以总结为:

共享对象:让多人可以访问公共状态 权限对象:限制谁能执行敏感操作

没有权限设计的共享对象很容易出问题。尤其是涉及价格、余额、收益、管理员操作、状态重置时,必须清楚区分普通用户操作和管理员操作。

11. 共享对象适合哪些场景

Shared Object 适合表达多人共同访问和修改的状态。

典型场景包括:

链上商店 交易市场 流动性池 投票系统 公共注册表 多人游戏房间 拍卖合约 共享计数器 公共配置状态

这些场景都有一个共同点:多个用户需要访问同一个对象,并且该对象的状态会随着不同用户交易不断变化。

例如,交易市场中,很多用户都可能挂牌、购买、取消订单。市场对象本身就不适合归某一个普通用户所有。

再比如,流动性池中,很多用户都可能添加流动性、移除流动性、进行 swap。池子状态必须作为共享对象存在。

因此,Shared Object 的核心适用条件是:

同一个链上状态需要被多个用户共同访问或修改

12. 哪些场景不适合使用共享对象

Shared Object 虽然强大,但不应该滥用。

如果一个对象只属于某个用户,不需要其他用户共同修改,就更适合使用 Owned Object。

例如:

个人 NFT 个人凭证 个人配置 个人计数器 个人钱包中的 Coin 个人游戏道具

这些对象没有必要设计成共享对象。

滥用共享对象可能带来几个问题:

第一,交易路径更复杂。
涉及共享对象的交易通常需要共识排序。

第二,权限设计更困难。
共享对象能被多人访问,必须认真设计哪些函数开放、哪些函数受限。

第三,并发冲突更多。
多个用户同时操作同一个共享对象,容易出现顺序依赖。

第四,状态膨胀风险更高。
公共对象如果承载太多状态,会影响合约可维护性。

因此,设计对象时可以先问一个问题:

这个对象是否真的需要被多个用户共同修改?

如果答案是否定的,就优先使用 Owned Object。

13. Shared Object 的常见错误

初学共享对象时,容易遇到一些错误。

第一个错误:忘记 share_object。
只创建了对象,但没有把它共享出去。结果其他用户无法作为共享对象访问。

第二个错误:把本应私有的对象设计成共享对象。
这会增加不必要的复杂度。

第三个错误:没有权限控制。
共享对象的敏感函数如果不需要任何权限对象,可能导致任何人都能修改关键状态。

第四个错误:误以为共享对象没有 owner 就没有安全边界。
共享对象的安全边界主要由 Move 类型系统、函数签名和权限对象共同实现。

第五个错误:并发顺序没有考虑清楚。
如果多个用户同时操作共享对象,交易顺序可能影响结果,合约逻辑必须能正确处理。

第六个错误:用共享对象保存过多用户私有状态。
如果每个用户的数据都塞进一个共享对象,后续维护和扩展会很困难。更好的方式可能是共享对象保存公共索引,用户状态使用 owned object 或动态字段组织。

14. 如何判断该用 Owned Object 还是 Shared Object

可以用几个问题判断。

第一个问题:这个对象是否只归某个用户使用?

如果是,使用 Owned Object。

第二个问题:是否有多个用户需要直接修改同一个对象?

如果是,考虑 Shared Object。

第三个问题:这个对象是否代表公共系统状态?

如果是,考虑 Shared Object。

第四个问题:是否需要权限对象控制管理操作?

如果是,可以采用 Shared Object + Capability 的设计。

第五个问题:是否可以把公共状态和用户私有状态拆开?

如果可以,通常不要把所有状态都放进一个共享对象。

可以用一个简单表格总结:

适合 Owned Object: 个人资产 个人凭证 个人配置 个人计数器 个人道具 适合 Shared Object: 市场 商店 投票系统 流动性池 拍卖 公共注册表 多人游戏状态

这套判断方式对后面设计复杂 Move 合约很有帮助。

15. 共享对象和 PTB 的关系

上一期讲过 PTB。PTB 可以把多个对象操作组合成一笔交易。

当 PTB 中涉及共享对象时,就需要注意交易排序问题。

例如,一个购买流程可能是:

1. SplitCoins:拆出付款 Coin 2. MoveCall:调用 shop::buy(shared_shop, payment) 3. TransferObjects:把返回的商品对象转给用户

这里的 shared_shop 是共享对象,因为所有用户都可以从同一个 shop 中购买商品。

这类 PTB 的特点是:

PTB 内部可以组合多个步骤 但只要涉及共享对象,就需要进入共识排序

所以,PTB 和共享对象经常一起出现。复杂应用通常既需要共享对象表达公共状态,也需要 PTB 组合多个交易步骤。

可以这样理解:

Shared Object 解决多人访问同一状态的问题 PTB 解决一笔交易中组合多个操作的问题

两者结合起来,就能表达很多真实应用场景。

16. 一个共享商店的简化理解

为了进一步理解,可以用一个共享商店作为例子。

链上有一个 DonutShop 对象:

DonutShop price = 10 balance = 0

这个对象是共享的,因此所有用户都可以调用 buy。

用户 A 买一个甜甜圈:

输入: - Shared DonutShop - Coin 10 执行: - 检查付款金额 - 增加 shop.balance - 创建 Donut 对象 - 转移 Donut 给用户 A

用户 B 也可以买:

输入: - Shared DonutShop - Coin 10 执行: - 增加 shop.balance - 创建 Donut 对象 - 转移 Donut 给用户 B

店主提取收益时,需要 ShopOwnerCap:

输入: - Shared DonutShop - ShopOwnerCap 执行: - 检查权限 - 提取 shop.balance - 转移收益给店主

这个例子体现了共享对象设计的三个核心:

DonutShop 是共享对象,承载公共状态 Donut 是用户购买后获得的 owned object ShopOwnerCap 是权限对象,限制店主操作

这就是共享对象在实际应用中的典型组合。

17. 小结

这一期主要讲了 IOTA 中的共享对象。

Owned Object 适合表达个人状态或个人资产,Shared Object 适合表达多个用户共同访问和修改的公共状态。共享对象可以通过share_object创建,创建后可以被多个用户作为交易输入。

不过,共享对象也更复杂。因为多个用户可能同时修改同一个共享对象,所以涉及共享对象的交易通常需要通过共识排序,确保所有验证者按照相同顺序执行交易,从而得到一致状态。

设计共享对象时,需要重点考虑三个问题:

这个对象是否真的需要共享? 哪些函数可以开放给所有用户? 哪些敏感操作需要权限对象控制?

可以用一句话总结本期内容:

Shared Object 让 IOTA 可以表达多人共同访问的链上状态,但它也要求开发者认真处理并发顺序、权限控制和状态设计。

下一期,我们会继续讲 IOTA EVM 与 MoveVM。重点回答:IOTA 为什么同时存在 EVM 和 MoveVM?Solidity 开发者和 Move 开发者分别应该如何理解这两套体系?

http://www.rkmt.cn/news/1462367.html

相关文章:

  • 上海牛肉汉堡品牌加盟推荐:现煎现烤工艺优势解析 - 17322238651
  • PyTorch图像增强避坑指南:ColorJitter里hue参数设置为什么不能超过0.5?一次搞懂HSV色彩空间
  • YY/T0681.5-2010气泡法检漏标准详解、取样数量要求
  • JAVA EE初阶---DAY 1 计算机是如何工作的
  • 3大核心优势+7步实战:SPT-AKI存档编辑器完全指南
  • Arduino I²C EEPROM存储实战:从24LC512原理到可靠数据读写
  • PyWxDump终极指南:如何安全备份与导出微信聊天记录
  • 深度解析IDM激活脚本的系统集成架构与安全实现方案
  • 6.4
  • 圆偏振光屏幕保护膜技术原理深度解析——从偏振光学到 scinique® 1.0 双护方案
  • 上海APP开发公司哪家性价比高?企业做APP定制开发怎么选?
  • PortSwigger SQL注入LAB11
  • DC-DC转换器在线测量电池交流内阻:下采样与FIR滤波算法实践
  • 终极B站视频下载指南:BilibiliDown让你轻松保存任何B站视频
  • 腾讯云原厂采购 VS 官方代理合作,企业选型参考指南
  • 杭州婚恋服务机构盘点:合规服务与匹配能力对比 - 互联网科技品牌测评
  • 自适应双频段能量采集电路设计:提升水下物联网设备续航能力
  • 3大核心技术突破:如何在NVIDIA显卡上实现AMD FSR 3帧生成技术
  • LD3320语音识别模块开发包:含DXP原理图、STC51例程、串口调试工具与实操录像
  • 10分钟搞定UltraStar Deluxe:跨平台卡拉OK游戏快速上手指南
  • 江苏切削液厂家实力盘点:五家头部供应商客观对比 - 奔跑123
  • 探索开源放射治疗计划系统matRad:从算法研究到临床教学的全新视角
  • 爱玛电动车实拍图集:853张高清原图+Pascal VOC格式XML标注,专为YOLOv5训练优化
  • 不计得失的人生智慧与心性修养
  • 从钉钉到飞书,AI请假集成全链路拆解,HRBP私藏的7步上线 checklist
  • NAS能跑大模型吗?GLM-5与Phi-3的现实边界
  • 绩效数据孤岛正在杀死AI投资回报率!——打通OKR、LMS、CRM与AI分析平台的4层API治理架构
  • 收藏!211本科985硕面试淘天AI开发二面经验分享,助你多拿3个offer!
  • Pixelorama:3步成为像素艺术大师的免费开源工具指南
  • AI工具接入融资流程的“死亡交叉点”:第37天必现的数据孤岛危机与5步熔断机制(附银行级审计日志模板)