news 2026/9/10 23:25:20

Linera 处理资产的应用:基于临时链实现原子交换与资产安全回收

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linera 处理资产的应用:基于临时链实现原子交换与资产安全回收

Linera 处理资产的应用:基于临时链实现原子交换与资产安全回收

【免费下载链接】linera-protocolMain repository for the Linera protocol项目地址: https://gitcode.com/GitHub_Trending/li/linera-protocol

导读

在 Linera 的多链架构中,把 Token 发送到由他人拥有的链上往往意味着把资产"托管"给对方:如果对方不处理你的消息,你就无法取回资产。本文基于 docs/developers/advanced_topics/assets.md 的核心思路,讲解如何利用临时链(temporary chains)所有权变更应用权限收窄构造一个"只能进、必须出"的托管环境,并深入剖析 matching-engine 示例 如何通过close_chain机制在关闭链时自动返还全部资产,最终实现无需信任第三方的原子交换。读完本文,你将掌握linera change-ownershiplinera change-application-permissions两个系统命令的组合用法,以及应用侧close_chain的标准实现模式。

问题背景:跨链资产托管中的可用性风险

在 Linera 上,资产(例如fungible应用的 Token)可以跨链转移。但这里有一个容易被忽视的信任假设:

如果你把 Token 发送到一条由他人拥有的链上,你就依赖对方来保证资产可用。如果对方不处理你的消息(例如拒绝出块、不执行你的取回请求),你就无法访问自己的 Token。

也就是说,单纯把资金"放"到别人的链上,并不是一个安全的行为。资产在到达目的地后,能否被取回,完全取决于该链的所有者是否配合。这一点正是所有"先托管、后结算"类 DeFi 交互(例如订单簿撮合、原子交换)必须解决的核心问题。

解决方案:用临时链构建"强制回收"的托管环境

Linera 为此提供了一套基于临时链的解决方案。它的适用前提是:参与方的数量有限,且在交互开始前就已知。满足这个前提后,我们可以按以下三个步骤改造一条链:

第一步:让所有参与方都成为链的所有者

使用linera change-ownership命令,把参与交换的所有方都设置为该链的 owner:

linera --wait-for-outgoing-messages change-ownership \ --owners "{\"$OWNER_1\":100,\"$OWNER_2\":100}"

--owners参数是一个 JSON 对象,键为 owner(账户地址),值为权重。所有权变更后,每位参与方都拥有在该链上提出区块(propose block)的权利,因此任何一方都不再能够单方面阻止他人"触碰"这条链上的资产

从源码结构看,ChangeOwnership是 Linera 的系统操作之一,其元数据在 linera-chain/src/data_types/metadata.rs 中定义为包含super_ownersowners(owner 与 weight 的键值对)、first_leadermulti_leader_roundsopen_multi_leader_roundstimeout_config的结构。权重(weight)决定了该 owner 成为轮次领导者(round leader)的频率。

第二步:只允许一个应用的 Operation 在链上执行

使用linera change-application-permissions命令,把链上的应用执行权限收窄到唯一的应用:

linera --wait-for-outgoing-messages change-application-permissions \ --execute-operations "[\"$MATCHING_ENGINE\"]" \ --manage-chain "[\"$MATCHING_ENGINE\"]"

这里有两个关键参数:

参数含义
--execute-operations允许在该链上执行 operation 的应用白名单。只放行撮合引擎应用后,其他应用无法再往链上"塞"操作。
--manage-chain允许管理该链(例如关闭链、变更所有权、变更权限)的应用白名单。

也就是说,这条临时链此后只接受两类"动作":matching-engine 应用的 operation,以及 matching-engine 应用发起的链管理操作。从 linera-sdk/src/contract/runtime.rs 可以看到,应用侧也暴露了对应的change_application_permissions方法,运行时的ApplicationPermissions::new_single(app_id)这种"单一应用权限"构造方式(见下文测试代码)正是这条命令的底层模型。

第三步:只允许关闭链这一个"出口"

当链被收窄为单一应用可操作之后,关闭链的操作也必须由该应用独占。这正是--manage-chain只填入$MATCHING_ENGINE的原因:除了撮合引擎自身,没有其他任何一方能够关闭这条链。

这样,整条临时链的语义就变成了:

  • 资金只能通过撮合引擎的 operation 流入;
  • 资金只能通过撮合引擎的 operation 流出;
  • 链的"终结"(关闭)也由撮合引擎全权控制。

源码纵深:matching-engine 如何实现"关闭即清退"

docs/developers/advanced_topics/assets.md明确指出:"处理资产的应用应当设计一个专门用于关闭链的 operation 或 message——当该操作被执行时,它应当把链上剩余的全部资产发还出去,并调用运行时的close_chain方法。"

examples/matching-engine/src/contract.rs 给出了完整实现。应用定义了Operation::CloseChain变体(见 examples/matching-engine/src/lib.rs),在execute_operation中处理:

async fn execute_operation(&mut self, operation: Operation) -> Self::Response { match operation { // ... Operation::CloseChain => { for order_id in self.state.orders.indices().await.unwrap() { match self.modify_order(order_id, ModifyAmount::All).await { Some(transfer) => self.send_to(transfer), // Orders with amount zero may have been cleared in an earlier iteration. None => continue, } } self.runtime .close_chain() .expect("The application does not have permissions to close the chain."); } } }

该实现的逻辑分两步:

  1. 清退全部在册订单:遍历状态中所有订单的索引(self.state.orders.indices()),对每个订单调用modify_order(order_id, ModifyAmount::All),即把订单对应的全部代币(无论是已挂出的买单还是卖单)按Transfer记录通过send_to发还到各自的 owner 账户。注释专门解释了为什么可能返回None:数量已清零的订单可能在更早的迭代中被清理过,此时直接continue跳过。
  2. 关闭链:调用self.runtime.close_chain()close_chain是 linera-sdk/src/contract/runtime.rs 提供的运行时方法:
/// Closes the current chain. Returns an error if the application doesn't have /// permission to do so. pub fn close_chain(&mut self) -> Result<(), ManageChainError> { contract_wit::close_chain().map_err(|error| error.into()) }

注意方法签名返回Result<(), ManageChainError>:如果应用没有--manage-chain授予的权限,调用会直接失败。因此示例代码里用.expect(...)显式暴露"没有权限关闭链"这一失败路径。

从源码结构看,这一"关闭即清退"模式并非 matching-engine 独有:同样处理Operation::CloseChain的还有 examples/amm/src/contract.rs(并在 第 611 行 明确拒绝了在远端链上执行关闭操作)。这可以推断:关闭链的操作只能在应用所在的"主链"上执行,跨链消息形式关闭会被拒绝

链关闭之后:在途资产依然可以返回

一个容易被忽略的关键设计是——关闭链并不会立刻阻止所有消息

一旦链被关闭,owner 仍然可以创建区块来拒绝(reject)消息。这样一来,即使是传输中的(in-flight)资产,也可以被退回到发送方。

这一点保证了安全性闭环:关闭操作是"在册资产清退 + 封链",而关闭之后新到达的跨链消息(例如某笔转账还在路上)可以通过拒绝消息的方式原路退回,而不是永久卡在一条已经关闭的链上。因此,无论资产是"已在链上"还是"正在路上",最终都有确定的回收路径。

实战演练:用临时链完成原子交换

下面的完整流程来自 examples/matching-engine/README.md,它把上文的三步改造落到了真实的 CLI 与 GraphiQL 操作上,可以直接复现一次原子交换。

环境准备

在仓库根目录构建好linera*工具链并启动本地网络(也可连接 testnet):

export PATH="$PWD/target/debug:$PATH" source /dev/stdin <<<"$(linera net helper 2>/dev/null)" FAUCET_PORT=8079 FAUCET_URL=http://localhost:$FAUCET_PORT linera_spawn linera net up --with-faucet --faucet-port $FAUCET_PORT

初始化钱包并申请三条链(对应三个 owner):

export LINERA_WALLET="$LINERA_TMP_DIR/wallet.json" export LINERA_KEYSTORE="$LINERA_TMP_DIR/keystore.json" export LINERA_STORAGE="rocksdb:$LINERA_TMP_DIR/client.db" linera wallet init --faucet $FAUCET_URL INFO_1=($(linera wallet request-chain --faucet $FAUCET_URL)) INFO_2=($(linera wallet request-chain --faucet $FAUCET_URL)) INFO_3=($(linera wallet request-chain --faucet $FAUCET_URL)) CHAIN_1="${INFO_1[0]}" CHAIN_2="${INFO_2[0]}" CHAIN_3="${INFO_3[0]}" OWNER_1="${INFO_1[1]}" OWNER_2="${INFO_2[1]}" OWNER_3="${INFO_3[1]}"

发布两个fungible应用(作为被交换的两种 Token),再发布 matching-engine 应用并传入两个 Token 的应用 ID:

FUN1_APP_ID=$(linera --wait-for-outgoing-messages \ project publish-and-create examples/fungible \ --json-argument "{ \"accounts\": { \"$OWNER_1\": \"100.\", \"$OWNER_2\": \"150.\" } }" \ --json-parameters "{ \"ticker_symbol\": \"FUN1\" }" \ ) FUN2_APP_ID=$(linera --wait-for-outgoing-messages \ project publish-and-create examples/fungible \ --json-argument "{ \"accounts\": { \"$OWNER_1\": \"100.\", \"$OWNER_2\": \"150.\" } }" \ --json-parameters "{ \"ticker_symbol\": \"FUN2\" }" \ ) MATCHING_ENGINE=$(linera --wait-for-outgoing-messages \ project publish-and-create examples/matching-engine \ --json-parameters "{\"tokens\":["\"$FUN1_APP_ID\"","\"$FUN2_APP_ID\""], \"price_decimals\": 2}" \ --required-application-ids $FUN1_APP_ID $FUN2_APP_ID)

--wait-for-outgoing-messages会等待法定数量的验证者确认所有已发出的跨链消息均已送达。

将链 1 改造成临时托管链

kill %% && sleep 1 # 先停掉 service,以便用 CLI 操作链 1 linera --wait-for-outgoing-messages change-ownership \ --owners "{\"$OWNER_1\":100,\"$OWNER_2\":100}" linera --wait-for-outgoing-messages change-application-permissions \ --execute-operations "[\"$MATCHING_ENGINE\"]" \ --manage-chain "[\"$MATCHING_ENGINE\"]" linera service --port $PORT &

这三条命令完成后,链 1 就变成了一条只有 owner 1、owner 2 可以出块,且只能由 matching-engine 应用执行操作和关闭链的临时链。

执行原子交换

启动 GraphiQL 服务后(linera service --port 8080),按以下步骤操作:

  1. Owner 2 先把自己的资产从链 1 领取到自己的链上(分别在 FUN1、FUN2 两个应用的 GraphiQL 页面执行claim,将链 1 上属于自己的 100 FUN1 与 150 FUN2 领到$CHAIN_2)。
  2. Owner 1 挂单:在链 1 的 matching-engine 页面提交一个 Bid(用 1 FUN1 买 5 FUN2):
mutation { executeOrder( order: { Insert : { owner: "$OWNER_1", quantity: "1", nature: Bid, price: { price: 500 } } } ) }
  1. Owner 2 挂单成交:Owner 2 提交一个 Ask(卖 2 FUN1 换 10 FUN2)。该订单会与 owner 1 的 Bid 部分撮合:owner 2 以 5 FUN2 的价格卖出 1 FUN1,剩余 5 FUN2 留在链 1 上。

  2. 关闭链并自动清退:此时链 1 上还锁着 owner 2 的 5 FUN2,唯一的取回方式就是由 matching-engine 关闭链:

mutation { closeChain }

closeChain会触发上文分析的Operation::CloseChain逻辑:撮合引擎遍历链上所有订单,把尚未成交的代币全部发还给对应 owner,然后调用close_chain。验证余额即可确认:owner 2 应拿回全部资金,最终在链 2 上拥有 145 FUN2。

这就是原子交换的核心保证:你在任何时候都可以拿回自己投入的资产——要么是还挂着的订单代币(通过关闭链),要么是已经成交换回的代币(通过成交结算)。由于链的关闭权被收窄到了撮合引擎应用,任何一方都无法用"拒绝出块"来永久扣押资金。

测试佐证:关闭链后的余额断言

该流程在仓库测试中有直接对应的验证。examples/matching-engine/tests/transaction.rs 展示了完整的"关闭链收尾"测试:

let permissions = ApplicationPermissions::new_single(matching_id.forget_abi()); matching_chain .add_block(|block| { block.with_change_application_permissions(permissions); }) .await; matching_chain .add_block(|block| { block.with_operation(matching_id, Operation::CloseChain); }) .await; user_chain_a.handle_received_messages().await; user_chain_b.handle_received_messages().await; // Check owner balances for (owner, user_chain, amount) in [ (owner_a, &matching_chain, None), (owner_b, &matching_chain, None), (owner_a, &user_chain_a, Some(Amount::from_tokens(4))), (owner_b, &user_chain_b, Some(Amount::from_tokens(6))), ] { assert_eq!(user_chain.query_account(token_id_a, owner).await, amount); }

注意测试中的两个细节:

  1. ApplicationPermissions::new_single(matching_id):与 CLI 的--manage-chain "[\"$MATCHING_ENGINE\"]"等价,即把链的管理权限收敛到唯一应用;
  2. 断言撮合链上余额为None:关闭链后,撮合引擎自己不再持有任何 token_a / token_b,所有资产都回到了 owner 的各自链上——这正是"关闭即清退"语义的自动化验证。

总结与适用边界

维度结论
核心问题把资产发送到他人拥有的链上,资产可用性依赖对方是否处理消息
解决前提参与方数量有限、事先已知(临时链模式)
三个改造步骤change-ownership让各方共持;change-application-permissions收窄到单一应用;应用内实现CloseChain操作
资产回收机制关闭链时应用遍历并清退全部在册资产 + 调用close_chain;关闭后 owner 仍可拒绝在途消息使其退回
典型应用订单簿撮合(examples/matching-engine)、AMM(examples/amm)中的原子交换与强制结算

需要强调的是,这套方案并不试图解决"任意数量参与方"的通用托管问题:一旦参与方集合是开放的、动态的,临时链的所有权改造就不适用。但对于双方或多方之间的一次性资产交换,它是 Linera 上一种安全、可验证、可完全由应用代码控制清算过程的模式——关闭链的时机、顺序、清退范围都写在应用自己的execute_operation里,任何一方都无权绕过。

【免费下载链接】linera-protocolMain repository for the Linera protocol项目地址: https://gitcode.com/GitHub_Trending/li/linera-protocol

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 23:22:25

AI论文写作工具测评:提升学术效率与合规性

1. 项目概述&#xff1a;AI论文写作平台测评的必要性写毕业论文是每个本科生都要经历的"成人礼"&#xff0c;但面对开题报告、文献综述、数据分析这些硬骨头&#xff0c;很多同学往往手足无措。去年指导学弟学妹论文时&#xff0c;我发现一个有趣现象&#xff1a;超过…

作者头像 李华
网站建设 2026/9/10 23:20:47

hello-algo 圖解佇列:FIFO 先入先出原理、雙端操作與多語言實作指南

hello-algo 圖解佇列&#xff1a;FIFO 先入先出原理、雙端操作與多語言實作指南 【免费下载链接】hello-algo 《Hello 算法》&#xff1a;动画图解、一键运行的数据结构与算法教程。支持简中、繁中、English、日本語&#xff0c;提供 Python, Java, C, C, C#, JS, Go, Swift, R…

作者头像 李华
网站建设 2026/9/10 23:20:01

CH376S+单片机读取CSV文件的嵌入式实现方案

简介&#xff1a;本资源是一套面向嵌入式开发者的CH376S芯片CSV文件读写实战工程&#xff0c;专为STM32F103RCT6平台设计&#xff0c;解决在资源受限MCU上通过USB外设&#xff08;CH376S&#xff09;高效处理结构化数据的核心需求&#xff0c;适用于工业数据采集、设备日志导出…

作者头像 李华
网站建设 2026/9/10 23:18:59

基于SpringBoot框架的程序设计竞赛平台的设计与实现(程序+文档+讲解)

温馨提示&#xff1a;本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;本人主页置顶文章(点我)开头有 CSDN 平台…

作者头像 李华