This is the old Algorand Developer Portal. Please head over to dev.algorand.co to explore our newly rebuilt documentation site. Please excuse us as we continue to transition content to the new portal

创建文章

We are looking for publications that demonstrate building dApps or smart contracts!
See the full list of Gitcoin bounties that are eligible for rewards.

Article Thumbnail

“签约”智能合约:安全的高级智能合约设计

我们已经详细阐述了怎样编写智能合约,细致描述了交易的结构。今天我们来深入了解交易头中默默嵌套的 Lease 字段暗藏的宝藏功能。我们将探索这一可选字段的具体功能,探讨何时要选择在智能合约中加入该字段,然后看一些示例代码和应用场景。

简介

Lease 字段是在 Algorand 2.0 中引入的,可用于保障偏好长期的专有交易执行(如定期付款)的安全,缓解费用波动,实现长期智能合约。我们会在本文中探索其中一些概念。从技术上讲,包含 Lease 值([32] 字节)的已确认交易将在验证节点上保留 { Sender : Lease } 数据对,直到其 LastValid 过期。这会创建一个“锁”,阻止任何未来交易在过期前使用同一 { Sender : Lease } 对。

典型的一次性付款资产“发送”交易生存时间很短,未必能从使用 Lease 值中获益,但如果在某些智能合约设计中没有定义这个值,可能会导致账户易受拒绝服务攻击。我们来看看为什么要考虑使用 Lease 字段,以及哪些时候绝对应该使用这一字段。

信息

快速回顾 Algorand 交易和智能合约验证。每个交易都包含一个,其中定义了交易类型,包含验证期间使用的必需字段和可选字段。FirstValidLastValid 是两个必需字段,定义交易被网络验证的范围(至多 1000 轮),超出即丢弃。通常,这相当于 MainNet 上至多 70 分钟的“有效窗口”。很多智能合约场景包含“目标”有效窗口计算,也在其验证逻辑中增设了特定的 Lease 值。这几项结合在一起,智能合约就能用于经常性的定期付款交易、密钥管理及其他场景。

Hello World

我们应该都很熟悉演示 Alice 发送几个 Algo 币给 Bob 的“hello world”样例了。这就是个生存期很短的交易,不太可能在正常网络条件下从指定 Lease 值中获益。使用 goal:

$ goal clerk send –from $ALICE –to $BOB –amount $AMOUNT

在正常网络条件下,该交易会在下一轮确认(约 5 秒)。Bob 从 Alice 处收到钱,就没有后顾之忧了。

但是,现在我们假设网络拥塞,费用高于正常水平,Alice 希望尽量减少费用支出,同时确保网络仅确认一笔给 Bob 的付款交易。Alice 可以构造一系列给 Bob 的交易,每笔都定义相同的 LeaseFirstValidLastValid 值,但增加 Fee 数额,然后广播到网络上。

# Define transaction fields
$ LEASE_VALUE=$(echo "Lease value (at most 32-bytes)" | xxd -p | base64)
$ FIRST_VALID=$(goal node status | grep "Last committed block:" | awk '{ print $4 }')
$ VALID_ROUNDS=1000
$ LAST_VALID=$(($FIRST_VALID+$VALID_ROUNDS))
$ FEE=1000

# Create the initial signed transaction and write it out to a file
$ goal clerk send –-from $ALICE –-to $BOB –-amount $AMOUNT \
–-lease $LEASE_VALUE --firstvalid $FIRST_VALID –-lastvalid $LAST_VALID \
–-fee $FEE –-out $FEE.stxn --sign

上面的代码中,Alice 定义了交易中使用的各项值。$LEASE_VALUE 必须是 base64 编码,且不能超过 32 字节(常以哈希值填充)。$FIRST_VALID 值从网络获取,而 $VALID_ROUNDS(此处设为最大)用于计算 $LAST_VALID$FEE 最初设为最小值,将是后续交易中唯一被修改的值。

Alice 现在通过 goal clerk rawsend –-filename 1000.stxn 广播初始交易,但由于网络拥塞和高昂的费用,goal 将持续等待确认,直至触及 $LAST_VALID。在验证窗口内,Alice 可构造更多几乎相同的交易(只是费用升高),并将这些交易同时广播出去。

# Redefine ONLY the FEE value
$ FEE=$(($FEE+1000))

# Broadcast additional signed transaction
$ goal clerk send –-from $ALICE –-to $BOB –-amount $AMOUNT \
                                    –-lease $LEASE_VALUE --firstvalid $FIRST_VALID –-lastvalid $LAST_VALID \
–-fee $FEE

每个后续交易 Alice 都会持续增加 $FEE 值。在某个时点,其中一笔交易将获批,很可能是当时费用最高的那笔,此时 { $ALICE : $LEASE_VALUE } 对上“锁”,直至触及 $LAST_VALID。Alice 可以确保之前提交的未决交易均不会通过验证。Bob 只收到一次付款。

潜在缺陷

这是个相当简单的场景,正常网络条件下不太可能发生。接下来,我们揭示 Alice 需要防范的几个安全隐患。一旦广播了初始交易,Alice 就必须确保所有后续交易采用完全相同的 FirstValidLastValidLease 值。注意第二笔交易中仅 Fee 递增了,确保其他值保持不变。如果 Alice 执行初始代码块两次,$FIRST_VALID 值会随即时查询网络而更新,因而延长评估 $LEASE_VALUE 的验证窗口。

与之类似,如果 $LEASE_VALUE 在静态验证窗口内被改变,可能会有多笔交易得到确认。谨记,“锁”是 { Sender : Lease } 的互斥;改变其中任何一个都会创建新锁。

验证窗口过期后,Alice 就可以在任何新交易中自由重用 $LEASE_VALUE 了。这是定期付款的常见做法。

智能合约设计

下面列出几个利用 Lease 字段的智能合约用例:代理密钥注册、定期付款和动态费用。在智能合约中纳入 Lease 评估主要出于两个设计考虑:费用最小化和定期交易。这两个概念可以在一个智能合约中结合起来,但我们留给读者去实现,并在开发者论坛上分享实现结果。

代理密钥注册

代理密钥注册演示阐释了利用 LogicSig 或“代理”脚本进行定期密钥轮换的模板的功能和用法。参数部分解释模板预期的值。这种情形中重要的是分别用于评估 LeaseFirstValidLastValidTMPL_LEASE``、``TMPL_PERIODTMPL_DURTMPL_LEASE([32] 字节)必须是 base64 编码,且在使用此 LogicSig 的所有交易中保持不变。TMPL_PERIOD 定义新验证窗口开放前需通过的轮数。TMPL_DUR 定义验证窗口保持开放的时长(至多 1000 轮)。此用例中 Lease 有益的原因在于,可以防止在一个验证窗口中批准多笔交易,避免额外的费用支出抽空账户。

定期付款

定期付款教程使用 PyTeal,为读者演示如何创建定期付款代理签名智能合约 (LogicSig)。这在模板字段上与代理密钥注册非常类似,但验证交易类型是付款而非密钥注册。tmpl_amt 指定所有交易的固定数额,所以 Lease 字段就用于确保每个验证窗口内仅一笔交易可能获批。这种智能合约使用固定费用,如果网络在任意验证窗口内要求更高的费用,接收方就容易遭到拒绝服务。下一个例子解决这一问题。

动态费用

动态费用教程使用 PyTeal,基于此模板演示。此用例旨在创建原子交易组,以使发送方能够得到与发送资金相关的所有费用补偿。因为未来费用定价的不确定性,这一点对发送方很有利。此 LogicSig 使用 Lease 抵御重放攻击,与上述代理密钥注册类似。

总结

Lease 字段可有力保障账户免遭重放攻击,令智能合约适用于执行经常性的定期交易。我们有很多资源可供设计安全智能合约时参考,也有很多 SDK 可以辅助实现。