波卡资产中心的可互换资产技术解析

本文深入探讨波卡资产中心的可互换资产技术实现,详细解析本地资产与外部资产的区别、XCM多位置标识符的优势、资产传输关系模型以及实际XCM编程示例,为开发者提供完整的技术架构理解。

波卡资产中心的可互换资产:技术深度解析

绝大多数人习惯通过名称或符号来识别资产,比如"Tether"或"USDT"。如果你在以太坊生态中待过一段时间,可能已经熟悉以零开头的合约地址。而波卡的资产中心直接在协议中托管资产功能,使用简单的整数作为资产ID。“1984"这样的命名,抛开戏谑成分,确实比0xdAC17F958D2ee523a2206206994597C13D831ec7更容易让人记忆和验证。现在波卡又推出了这个资产功能的另一个并行实例,不同的是这个实例使用称为"多位置"的XCM原语来识别资产。

在撰写这篇解析文章时,我希望传达这样的观点:这为波卡网络内部及其可达的任何地方的资产利用开辟了富有表现力和强大功能的模式。

本地资产与外部资产

当资产中心首次推出时,它只托管了一个资产托盘实例,允许任何人声明可用的资产ID并创建自己的资产。与每个资产使用自定义合约不同,资产中心嵌入了资产逻辑,将它们视为一等原语。每个资产都具有相同的功能。

这些具有可声明的、基于整数的资产ID的资产被称为"本地资产”。资产中心主要服务于这些资产的创建者,通常是像USDT这样的储备支持的稳定币。然而,协议只强制资产ID的唯一性(在这种情况下是一个整数)。创建者可以设置资产符号等元数据。因此,用户仍然需要对自己的资产进行尽职调查;任何人都可以称自己的资产为USDT,但用户可能只想要由Tether创建的那个。

资产中心充当资产创建者的"管理门户",允许他们铸造和销毁代币,并获取在整个波卡网络中总发行量的概览,包括那些已发送到网络中其他位置的代币。

但资产ID本身并不具有很强的表现力。虽然比合约地址更容易验证,但ID并没有向用户传达任何关于资产的信息。这时,XCM登场了。

多位置

多位置表达相对路径。它们是相对的,因为它们依赖于解释的位置。“如何去超市"会根据起始位置有不同的方向。在最基本的层面上,这些路径表达了到其他链的方向。但它们可以表达几乎任何事物的方向:资产、合约、托盘索引、治理机构、账户等。

多位置有一系列连接点,通常表示为两个部分:首先是若干"父级”,其次是从那里开始的路径。一个多位置的例子是"父级:1,内部:平行链(9,000)"。这表示"去往我的父级,然后从那里去往平行链9,000"。在这种语境中,“父级"是一个包含性的共识系统。例如,中继链是平行链的包含性共识系统。平行链可以是智能合约的包含性共识系统。在这个例子中,多位置来自另一个平行链,比如资产中心,这是合理的。平行链9,000将是一个兄弟链,因为它们共享同一个父级,即中继链。

作为资产标识符,多位置相比绝对(如地址、哈希、整数)标识符具有显著优势。主要是,资产的多位置本身标识了控制实体。在上面的例子中,那将是平行链9,000的治理。当查看绝对标识符时,用户必须信任发行实体及其声明,例如链上代币与链下资产是一一对应的。终止于平行链、智能合约或其他协议的多位置实际上指示了控制资产的逻辑。这并不能免除用户所有必要的尽职调查,例如平行链9,000可能有一个受信任的"超级用户”。但多位置告诉用户这个资产是由一个协议控制的,并且是哪一个协议。

除了多位置的终点,它实际上明确了一个"指挥链"。举一个更长的例子,比如平行链9,000上某个托盘内ID为42的资产——“父级:1,内部:平行链(9,000),托盘索引(99),通用索引(42)"。这个资产由托盘控制,托盘在平行链的共识内,平行链在共享父级(中继链)的共识内。多位置甚至可以表达完全外部的共识系统,例如"父级:2,内部:全局共识(以太坊)"。从平行链的角度来看,这表示"向上两级(即中继链之上)然后进入以太坊的共识”。

注意这些位置与Unix文件路径非常相似,例如"../Parachain(9000)/PalletIndex(99)/GeneralIndex(42)“或”../../GlobalConsensus(Ethereum)"。

最终结果是波卡的资产中心可以表示从波卡可达的任何资产。无论是通过本地托盘或合约调用、XCMP,还是通过桥接,无论是协议原生代币还是来自其他链的本地资产,资产中心为所有资产提供了一个通用接口,其中资产的标识符实际上告诉了你它的主权位置。

关系

XCM语言有两种方式表达位置/资产对的资产转移关系:传送和储备。这些定义了资产中心与其他链的关系以及它们如何交互。

传送很简单。当两个链对某个资产互相信任时,发送者可以简单地销毁它并向接收者发出铸造指令。只要发送者信任接收者不会铸造超过发送的数量,发送者可以接受相同的传送指令作为回报。

储备更复杂。当资产起源的链不信任另一个链时,它可以将资产本地放入目标链的主权账户,并向目标链发送消息,告知已在其本地账户中记入该资产。然后目标链可以为其用户铸造衍生资产。当处理完这些资产后,目标链可以发送消息回起源链,指示其将资产移出账户(大概它已经销毁了相应的衍生发行)。

储备场景中的信任是单向的。铸造衍生资产的链信任发行链维持其主权账户余额并尊重赎回。但发行链不信任那些资产将要去的链会忠实地处理它们。

这里的一个重要点是信任关系存在于位置/资产对:也就是说,一个链可以信任另一个链作为某些资产的传送者,但不包括其他资产。

那么,谁信任谁?信任什么?在多位置范式中,实体总是信任它们的"父级"。例如,平行链8,000上的智能合约信任平行链8,000的治理,而平行链8,000信任波卡中继链。波卡中继链由"根起源"治理,它可以执行任何指令,包括踢出一个平行链。波卡的根起源也治理其所有系统平行链(事实上,中继链加上所有系统平行链可以被视为单一的"波卡协议")。

波卡网络中的所有链和子协议(例如智能合约)都信任波卡协议,因此它们应该可以与之传送资产。事实上,使用储备将是相当愚蠢的:如果波卡协议不喜欢它在起源链上的储备余额,它可以通过根起源公投,直接将余额覆盖为它喜欢的值。

另一方面,波卡协议不能将这种普遍信任扩展到其中的成员。但它可以信任一个位置来管理源自该位置的资产。例如,它可以信任平行链9,000及其原生代币(PNT,“pints”?)以及在其中创建的资产,例如本地发行的代币。因此,当与平行链9,000交互时,资产中心将传送PNT,尊重PNT源自该平行链的事实。但是,仍然与平行链9,000一起,资产中心将对PET(平行链8,000的代币,发音较不模糊)使用储备转移。

选择储备位置

PET的创建受平行链8,000的治理,它接受波卡协议的治理。因此,波卡自然信任平行链8,000处理PET,因为PET构成了平行链8,000协议的一部分。但波卡和平行链8,000都不信任任何其他平行链*作为PET的储备位置。

这个概念沿着指挥链继续向下,到平行链8,000内创建的其他资产。事实上,它与独立的链或异步性无关;同一链上的两个智能合约可能不信任彼此管理对方的资产,但它们都信任它们存在的链。

给定这种双向信任关系,资产中心可以作为传送到它的资产的储备位置。平行链8,000可以将其PET传送到资产中心,然后资产中心可以作为储备位置,用于向其他位置和在这些位置之间的转移。意思是,平行链9,000可以使用资产中心作为其PET的储备,发送到其他平行链。

但这些其他位置现在可能将平行链8,000和资产中心都视为PET的储备位置。

在实践中,希望以这种方式使用资产中心的协议(平行链、智能合约等)将需要管理给定资产的多个储备位置的概念。务实地说,这可能意味着为每个资产选择一个储备位置。平行链和其他协议之间的共同协议和标准同样将简化它们的交互。

随着波卡网络中有数千个协议,与所有协议建立通信通道是笨拙的、不希望的或不切实际的。仅仅因为一个协议不希望与所有其他协议建立通道,它可能仍然希望自由访问资产。而且因为资产中心可以表示并作为任何从波卡网络可达(而不仅仅是在波卡网络内)的资产的储备位置,资产中心可以作为一个单一的储备位置,协议可以从中管理和交互几乎无限数量的资产。

实践

让我们看一个示例,说明如何编写一个XCM程序,将平行链的资产传送到资产中心。对于希望将此逻辑添加到其平行链的开发人员来说,有两件事应该突出。首先,XCM程序执行是从执行器实例的角度进行的,而不是程序的起源。这意味着应用程序应该发送从资产中心的角度引用资产和位置的程序。

其次,支付费用可能不是小事。当在系统链之间传送DOT或对在其主权账户上有DOT的链使用基于储备的指令时,这些应用程序可以使用它们正在交易的资产来支付费用。然而,资产中心可能不接受应用程序的资产用于支付费用,因此程序需要使用可接受的资产支付费用。资产转换的添加将使这更简单和灵活,但链仍然需要启动支持费用支付的对。

通过定义一些资产开始我们的程序,即DOT和平行链9,000(PINTs)的原生资产,以及一个接收资产的受益人:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// 从资产中心的角度定义原生资产。
let dot = MultiAsset {
    id: AssetId::Concrete(MultiLocation::parent()),
    fun: Fungibility::Fungible(10_000_000_000), // 1 DOT
};

// 从资产中心的角度看平行链9,000的位置。从平行链的角度
// 它可能引用其原生资产并重新锚定。
let para_nine_thousand = MultiLocation::new(1, Parachain(9000));
let hundred_pints = MultiAsset {
    id: AssetId::Concrete(para_nine_thousand),
    fun: Fungibility::Fungible(100),
};

// 我们希望所有资产去的受益人。即,发送者必须指定
// 他们想要存入资产的`AccountId`(`target_account`)。
let beneficiary = MultiLocation {
    parents: 0,
    interior: X1(AccountId32 { network: None, id: target_account.into() }),
};

在构建发送到资产中心的程序之前,发送者需要核算他们正在传送的资产。链也可以配置其XCM执行器以更优雅地处理这个问题。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
let assets: MultiAssets = hundred_pints.into();
let mut local_program_to_execute: Xcm<<T as frame_system::Config>::RuntimeCall> = Xcm(vec![
    // 提取你正在传送的资产(PINTs)。
    WithdrawAsset(assets.clone()),
    // 使用本地默认资产支付执行费用。
    SetFeesMode { jit_withdraw: true },
    // 销毁你正在传送的PINTs。
    BurnAsset(assets),
    // 提取并销毁用于费用的DOT。这里使用的资产取决于你的
    // 本地链如何表示储备支持的DOT。
    WithdrawAsset(/* local representation of DOT */),
    BurnAsset(/* local representation of DOT */),
]);
let outcome = T::XcmExecutor::execute_xcm_in_credit(local_program_to_execute, ..);
outcome.clone().ensure_complete().map_err(|_| Error::<T>::FailedToExecute)?;

现在,继续构建发送到资产中心的XCM程序:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// 最后,构建发送到资产中心的XCM程序。
let xcm_to_send = Xcm(vec![
    // 从发送者的主权账户提取DOT。用户应该
    // 在发送方支付这个,你的平行链需要保持其主权
    // 账户充足。
    WithdrawAsset(dot.clone().into()),
    // 用这个购买一些执行。
    BuyExecution { fees: dot.into(), weight_limit: Unlimited },
    // 现在我们可以接收传送的外部资产。
    ReceiveTeleportedAsset(hundred_pints.into()),
    // 退还任何权重盈余。
    RefundSurplus,
    // 将PINTs和剩余的DOT存入同一个受益人。
    DepositAsset { assets: AllCounted(2).into(), beneficiary },
]);

这个程序将从平行链的主权账户提取DOT,使用这个DOT购买执行此程序所需的权重,接收传送的PINTs,退还任何未使用的权重,最后将两个资产(从提取的DOT中剩余的任何零钱加上PINTs)存入受益人账户。

记住发送者可能在发送此消息之前有一些核算工作要做。这种类型的程序构建也不应该直接对用户可用,而是放在具有适当检查的外部函数后面。几乎可以肯定,发送者不是DOT的受信任传送者,否则他们会传送两种资产,并且可能没有DOT在他们的主权账户中提取。意思是,他们可能在其本地链上有一个衍生的、储备支持的DOT。从其主权账户提取这个DOT并将其移动到费用支付和受益人中将减少他们的储备。因此,发送者应该在发送此消息之前销毁这个储备支持的表示,以免其链在储备中没有全额抵押。发送者可以从发起传送的用户那里借记它,或者保持自己的DOT库从中提取(偶尔补充其储备)。有关更完整的示例,请参阅Trappist中完成的核算。

总结

向资产中心添加外部资产引入了新的范式,如多位置作为资产标识符和多个储备位置,但这些允许在网络内进行富有表现力和方便的交互。

Parity将在未来几个月发布更多示例和教程,以演示使用外部资产的一些常见模式。平行链开发人员应关注Rococo上的Trappist,钱包/集成开发人员应查看资产转移API。

对于勇敢者,Rococo、Kusama和波卡的资产中心已经支持外部资产,因此你可以开始开发工作。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计