
GearFans
2023/04/03阅读:25主题:默认主题
Gear 智能合约开发常用技术 2|Gear Wiki
提示
本篇文章介绍与 Gear 智能合约相关技术,帮助你更好的了解 Gear。

异步合约
Gear 程序之间的异步交互与通常的异步请求类似,在使用await
并通过发送消息来实现。
程序入口
如果一个程序使用异步信息,它的主要可执行函数就会改变。
async_init()
如果在程序初始化中存在异步调用,那么我们应该使用 async_init()
而不是 init()
。
#[gstd::async_init]
async fn init() {
gstd::debug!("Hello world!");
}
main()
对于异步信息也是如此,我们用 main
代替 handle
、handle_reply
。
#[gstd::async_main]
async fn main() {
gstd::debug!("Hello world!");
}
信息
async_init
可以和 async_main 一起使用。但如果使用这个宏,就不能指定init
和handle_reply
函数。
跨程序消息通讯
要向 Gear
程序发送消息,使用函数 send_for_reply(program, payload, value)
,在这个函数中:
-
program - 发送消息的程序的地址 -
payload - 程序的消息 -
value - 附在消息上的资金
pub fn send_for_reply_as<E: Encode, D: Decode>(
program: ActorId,
payload: E,
value: u128
) -> Result<CodecMessageFuture<D>>
创建合约
一个去中心化应用程序的业务逻辑可能需要程序(智能合约)有可能在网络中创建、初始化和启动一个或几个其他程序。当外部各方(用户)需要访问他们自己的典型智能合约的实例时,通过合约创建合约的需求是必要的。
我们以一个贷款功能的合约为例。在这种情况下,开发者可以创建一个工厂合约,该合约将按需创建贷款智能合约的实例并进行操作。
首先,要创建一个程序,必须使用 gear.uploadCode
将程序代码提交到网络并获取其代码哈希值。提交程序代码不会初始化程序。
信息
你可以通过 Gear IDEA 提交合约代码,也可以通过
@gear-js/api
SDK 提交。 甚至也可以通过Gear Program
命令行提交 —— https://github.com/gear-tech/gear-program。
代码提交后,可以用来创建一个新的合约:
use gstd::{prog::ProgramGenerator, CodeHash, msg};
#[no_mangle]
extern "C" fn handle() {
let submitted_code: CodeHash =
hex_literal::hex!("abf3746e72a6e8740bd9e12b879fbdd59e052cb390f116454e9116c22021ae4a")
.into();
// ProgramGenerator returs ProgramId
let program_id = ProgramGenerator::create_program_with_gas(submitted_code, b"payload", 10_000_000_000, 0).unwrap();
msg::send(program_id, b"hello", 0).expect("Unable to send message");
}
更多 gstd::prog
相关的内容,请看 https://docs.gear.rs/gstd/prog/index.html
Gas 预留
Gas 预留是 Gear 协议的强大功能,它为智能合约编程和现代用例提供了新方法。
简单地说,程序可以使用之前预留的 gas 来发送消息,而不是使用当前处理的消息中的 gas。
这个功能的关键优势是能够自动向网络中的任何参与者--用户或另一个智能合约以及本身 -- 发送延迟消息。事实上,一个程序能够自己执行无限的块(只要保持足够的 gas 即可)。
开发者可以在程序的代码中提供一个特殊的函数,从这个程序的可用量中获取一定数量的 gas,并将其保留。预留得到一个唯一的标识符,可以被程序用来获取这个预留的 gas 并在以后使用。 要保留 gas 的数量以便进一步使用,请使用以下函数:
let reservation_id = ReservationId::reserve(RESERVATION_AMOUNT, TIME)
.expect("reservation across executions");
还必须说明在哪个区块内使用该预留的 gas。gas 预留不是免费的:预留一个区块需要 100 个 gas。reserve
函数返回ReservationId
,它可以用来发送带有该 gas 的消息。使用预留 gas 发送消息:
msg::send_from_reservation(reservation_id, program, payload, value)
.expect("Failed to send message from reservation");
如果在预留时规定的时间内不需要 gas,可以取消预留,gas 将被退回给预订的用户。
id.unreserve().expect("unreservation across executions");
程序可以有不同的执行方式,改变状态并以某种方式进行评估,但当有必要时,程序可以用这个预留的 gas 发送消息,而不是使用自己的 gas。
例如,让我们考虑完全在链上工作的游戏。玩家是智能合约,通过实施各种游戏策略相互竞争。通常,在这些类型的游戏中,有一个主合约来启动游戏并控制玩家之间的移动顺序。为了开始游戏,有人向合约发送一条信息。这个信息所附带的 gas 被花在玩家的合约上,而这些合约又花在它们的执行上。由于游戏可以持续相当多的回合,所附带的 gas 可能不足以完成游戏。你可以发送消息要求程序继续游戏,或者你可以使用 gas 预留并进行全自动游戏。使用 gas 预留,合约将能够不间断地举行比赛。
延迟消息
智能合约在其他区块链上继续运行的通常方式是依靠外部的、中心化的资源。这意味着在被链上交易触发之前,这些合约的代码不会运行并对区块链的状态进行更改。
外部交易作为一个“戳”来激活智能合约并启动其逻辑。例如,我们可以通过向拍卖合约发送一个消息来启动拍卖。当拍卖时间过去后,合约将需要处理拍卖结果。然而,这将不会发生,直到有人向合同发送适当的消息来触发这一行动。
Gear Protocol 通过引入延迟消息传递功能解决了这个问题。Gear 网络中的智能合约能够执行无限数量的区块,只要有足够的 gas 来执行。gas 预留可以保证这一点。因此,在 dApp 中包含中心化组件的需求被消除了,允许它们完全在链上运行。
msg::send_delayed(program, payload, value, delay)
msg::send_bytes_delayed(program, payload, value, delay)
延迟消息将在指定的以块为单位的“延迟”之后执行。例如,在一个生成块时间为 2 秒的网络中,延迟 30 个块等于 1 分钟。
考虑到拍卖的例子,我们可以通过向拍卖合约发送消息来开始拍卖。在完成所有必要的逻辑后,拍卖合同将向自己发送一个延迟消息,这将在指定时间后解决拍卖问题。
一致性和可靠性
Gear Protocol 的关键特性之一是使用 Actor 模型进行消息传递通信。Actor 模型框架支持异步消息传递和并行计算,这极大地提高了 dapp 的可实现速度和可伸缩性。在 Actor 模型中,程序不共享状态,而是通过消息相互通信。如果一个程序向另一个程序发送异步消息,它必须等待另一个程序的回复,然后才能继续进行下一个操作。
当一个程序与另一个程序进行交互时,交易就变成了“分布式”。分布式交易是在多个数据库中执行的一组操作,或者在 Gear Protocol 的情况下,在具有自己状态的多个 actor 之间执行。分布式交易必须拥有这些属性:
-
原子性--所有数据更改都被视为单个操作。也就是说,要么进行所有修改,要么不进行任何修改; -
一致性 —— 这个属性意味着当一个交易开始和结束时,数据的状态是一致的。
例如,以太坊区块链上的交易是原子性的,这意味着如果交易由于错误而失败,它对全局状态的所有影响都将回滚,就像交易从未发生过一样。许多区块链应用程序依赖于交易的原子性,但当使用以太坊上的编程范式构建异步应用程序时,这可能是一个问题,因为你可能会遇到交易失败后无法恢复程序状态的问题。
考虑一个简单的代币互换,用户想用代币 A 互换流动性池中的代币 B。互换合约将向代币 A 合约发送一条消息,向代币 B 合约发送一条消息。如果这些消息中的一个成功,而另一个由于某种原因失败,那么代币 A 合约的状态将被改变,而代币 B 合约的状态将保持不变。这可能导致数据状态的不一致,并使其难以从失败的交易中恢复。因此,考虑实现分布式交易的不同编程范式是很重要的。
让我们使用代币互换的例子来看一下不同的编程方法。
将代币互换交易拆分为 3 个独立交易
考虑以下情况:我们有一个由代币 A 和代币 B 组成的流动资金池,也有一个用户想用他的代币 A 换取代币 B。
第1步
:用户向互互换合约发送一个 MakeOrder
消息。在该交易中,该合约向同质化代币合约发送一条消息。执行该消息的结果可能成功或失败。最坏的情况是在处理代币合约中的消息时,或在随后执行互换合约时,缺少 gas。然而,由于代币合约支持幂等性,用户可以简单地重启交易并完成交易。

第2步
:用户向互换合约发送一个 ExecuteOrder
消息。互换合约只是计算用户将收到的代币数量,并保存流动性资金池的最新状态。

第3步
:用户向互换合约发送一个Withdraw
消息,并收到代币 B。

在一个交易中执行一个互换是可能的。为了解决原子性问题,我们可以在这里使用以下模式:
-
2 PC -- 2阶段提交协议
(还有其扩展--3 阶段提交协议); -
Saga 模式
。
两阶段提交协议
理论 : 我们有一个向参与者发送消息的协调器。两阶段提交协议
有两部分:prepare
阶段和 commit
阶段。
准备阶段:
在准备阶段,协调器和参与者执行以下对话:
-
Coordinator
:
协调器指示每个参与的数据库服务器预提交交易。
-
Participants
:
每个参与者都会通知协调器,它是否可以提交其交易分支。
-
Coordinator
:
协调器根据每个参与者的响应,决定是提交还是回滚交易。只有当所有参与者都表明他们可以提交到他们的交易分支时,它才会决定提交。如果任何参与者表示它还没有准备好提交到它的交易分支(或者如果它没有响应),协调器决定结束全局交易。
提交阶段:
在提交阶段,协调器和参与者进行以下对话:
-
Coordinator
:
协调器将提交记录或回滚记录写入协调器的逻辑日志,然后指示每个参与者提交或回滚交易。
-
Participants
:
如果协调器发出了一个提交消息,参与者通过向逻辑日志写入提交记录来提交交易,然后向协调器发送一个确认交易被提交的消息。如果协调器发出回滚消息,参与者回滚交易,但不向协调器发送确认。
-
Coordinator
:
如果协调器发出消息提交交易,它将等待收到每个参与者的确认,然后再结束全局交易。如果协调器发出消息要回滚交易,它就不等待参与者的确认。
让我们看看如何在代币互换合约的例子中使用它。我们考虑以下情况:账户想用他的代币(我们称之为代币 A)互换其他代币(代币 B),使用互换合同中的流动性池。
在这种情况下,互换合同是一个协调器合约,而代币合约是参与者。
互换合约会做以下步骤:
准备阶段:
-
Swap contract
:
互换合约向代币合约发送消息,准备转移代币(消息可以并行发送)。事实上,代币合约在这个阶段必须锁定资金。
-
Token contract
:
代币合约进行所有必要的检查,在成功的情况下,锁定资金并回复互换合约,说他们已经准备好进行交易了。
-
Swap contract
:
互换合约处理来自代币合约的消息,并决定是否提交或中止全局交易。 接收代币 B,这里的情况与第一步相同。

提交阶段:
-
Swap contract
:
如果代币合约确认他们已准备好执行交易,则互换合约会向他们发送一条消息以提交状态。否则,互换合约会告诉他们中止交易。
-
Token contract
:
代币合约最终改变它们的状态并发送对互换合约的回复;
-
Swap contract
:
互换合约处理来自代币合约的消息并保存有关交易执行的结果。

当然,所有工作流都会处理消息执行期间 gas 耗尽的情况。
Pros
:优点
-
消息可以并行发送; -
如果考虑到缺少 gas 的情况,则可以实现数据一致性。
Cons
:缺点
-
参与者必须等待协调器的消息,他们不能自己提交或中止; -
协调器起着重要的作用:如果它未能发送消息,那么所有参与者都会进入阻塞状态(在我们的示例中:代币合约中的资金被阻塞)。
三阶段提交协议
理论:它类似于两阶段提交协议,但它试图解决阻塞参与者状态的问题,并为参与者提供自行恢复状态的机会。
准备阶段: 这里遵循两阶段提交协议的相同步骤:
-
Coordinator
: 协调器向所有参与者发送准备消息并等待回复; -
Participants
: 如果参与者准备好提交交易,他们会发送就绪消息,否则他们不会向协调器发送消息。 -
Coordinator
: 根据回复,协调器决定是否进入下一个状态。如果任何参与者没有响应消息或如果任何参与者未能在定义的时间内响应,协调器将向每个参与者发送中止消息。强调与两阶段提交协议的区别很重要:-
协调器限制参与者的响应时间。我们可以通过发送一条消息来实现这一点,其中包含指定的气体量或协调器准备等待的指定块数; -
如果协调器在此状态下失败,则参与者可以使用延迟消息中止交易(即解锁他们的状态)。因此,在那个阶段,超时情况会中止。
-
预提交阶段:
-
Coordinator
: 协调器向所有参与者发送一条预提交消息并得到每个人的确认; -
Participants
: 收到预提交消息,参与者知道一致决定提交。正如在准备阶段已经提到的,如果参与者未能及时收到此消息,则它会中止。但是,如果参与者收到中止消息,则它可以立即中止交易。
可能的问题:协调器在向参与者发送预提交时失败。所以一些参与者处于第 2 阶段,其他参与者处于第 1 阶段。这是一场灾难,因为第 1 组将提交,第 2 组将在超时的情况下中止。 所以我们必须确保如果其中一个参与者收到了预提交消息,他们都可以提交。如果协调器挂掉,处于第 2 阶段的任何参与者都可以自己成为协调器并继续交易。
-
Coordinator
: 收到所有参与者的确认后,协调器进入下一阶段。
三阶段提交协议完成两件事:
-
允许使用 recovery coordinator
(它可以是启动新交易的协调器本身,也可以是参与者)。如果协调器挂掉,恢复协调员可以查询参与者。
-
如果发现参与者处于第 2 阶段,则意味着每个参与者都已完成第 1 阶段并对结果进行投票。第 1 阶段的完成是有保证的。一些参与者可能已经收到提交请求(第 3 阶段)。恢复协调器可以在第 2 阶段安全地恢复。 -
如果参与者处于阶段 1,则意味着没有参与者开始提交或中止。该协议可以从头开始。 -
如果参与者处于第 3 阶段,协调者可以继续第 3 阶段——并确保每个人都收到提交/中止请求。
-
现在每个阶段都可以超时——没有像两阶段提交协议那样的无限期等待。
-
Phase 1
: 如果参与者没有及时收到协调员的消息,它就会中止; 如果没有收到任何参与者的消息,协调器会向所有参与者发送中止消息。 -
Phase 2
: 如果参与者等待协调器超时,则选举新的协调器。
让我们再回到互换合约。
准备阶段:
以下情况都有可能:
-
所有代币合约都收到消息; -
互换合约无法等待任何代币合约的响应 -
互换合约本身失败。
在失败的情况下,如果交易没有重新开始,互换合约将不会进入第二阶段,代币合约将使用延迟消息解锁它们的状态。

预提交阶段:
在这个阶段,我们可能会因为缺少 gas 而导致互换合约或代币合约失败。为了解决这个问题,我们可以使用 gas 预留,如下所示:
-
掉期合约在其 handle_signal
中接收到有关错误的信息; -
使用 gas 预留(因此,之前有必要关心 gas 预留),互换合约向自己发送消息,以从第 2 阶段重新开始交易。(同样的逻辑也可以用在 准备阶段
)。

提交阶段:
与前一阶段一样,我们只能因缺少 gas 而失败。在这里它不是那么重要,因为在这个阶段所有参与者都可以做出承诺。
Saga 模式
理论:
saga
是一系列本地事务的序列。每个本地事务更新数据库并发布消息或事件以触发 saga 中的下一个本地事务。如果本地事务因为违反业务规则而失败,则 saga 会执行一系列补偿事务,以撤消先前本地事务所做的更改。因此,Saga 由多个步骤组成,而 2PC
就像一个请求。
有两种协调 sagas 的方式:
-
Choreography
- 每个本地事务都会发布触发其他服务中本地事务的域事件; -
Orchestration` - 协调器(对象)告诉参与者要执行哪些本地事务。
我们将考虑orchestration based Saga
,其中将有一个编排器(互换合约)从一个中心管理整个操作。
互换操作包括以下步骤:
-
互换合约收到消息,兑换流动性池中的代币。因此,它必须将代币 A 从帐户转移到其地址,然后再将代币 B 转移给用户。 -
它创建了第 1 个任务:将代币从用户转移到掉期合约。它还为第一个任务创建补偿交易:将代币从掉期合约转移回用户。第二个任务是将代币从掉期合约转移给用户。 -
它开始执行第 1 个任务。如果执行失败,它会取消交易。如果成功,掉期合约执行第二个任务; -
如果第 2 个任务执行成功,则交易完成。否则,交换合约执行第一个任务的补偿交易。

需要注意的是,补偿性交易不应由于任何逻辑错误而失败。它们只能由于缺乏气体而失败。如果发生这种情况,那么你需要再次重启交易或使用 gas 保留。代币合约的idempotency
保证了交易将被完成到最后,不会出现重复交易。
关于 GearFans
Gear 是波卡生态的计算组件,GearFans 是 Gear 爱好者社区。
-
官网:https://gear-tech.io/ -
Twitter:https://twitter.com/gear_techs -
中文 Twitter:https://twitter.com/gear_fans -
Vara Twitter:https://twitter.com/VaraNetwork -
Vara Twitter CN:https://twitter.com/VaraNetwork_CN -
GitHub:https://github.com/gear-tech -
Discord:https://discord.com/invite/7BQznC9uD9 -
Medium:https://medium.com/@gear_techs -
Telegram 群:https://t.me/gear_tech -
Telegram 中文群:https://t.me/Gear_CN -
Telegram 中文开发群:https://t.me/gear_dev_cn -
Vara Telegram CN:https://t.me/varanetwork_cn
作者介绍

GearFans
Gear 是波卡生态的计算组件。