原文标题:Another Update on Stable Cadence
原文链接:https://forum.onflow.org/t/another-update-on-stable-cadence/3715
在 2022 年 1 月,Cadence 团队分享了关于 Stable Cadence 之路的想法。自那之后我们已经取得了长足的进步:我们发布了 Secure Cadence 里程碑,实现了无许可合约部署,同时我们也在继续推进 Stable Cadence 的里程碑和其他语言功能和改进。
在这篇文章中,我们想向大家介绍目前在 Stable Cadence 里程碑上 Flow 开发社区所取得的进展,以及明年年中左右将发布哪些内容,可以准备些什么。
我们还要感谢所有积极参与并做出贡献的社区成员,没有你们会更加困难。
已完成 / 已合并
■ View 函数 / 函数条件中的调用
https://github.com/onflow/flow/pull/1056
https://github.com/onflow/cadence/pull/1871
Cadence 增加了一个新的函数修饰关键字的支持:view,该关键词将强制函数体内不会发生 mutate 操作。 view 关键字在声明中需要放在 fun 关键字之前。
如果一个函数没有 view 注释,它将被认为“非视图”,这将与之前没有任何区别。 而如果函数有了 view 关键字声明,则不允许包含以下操作:
写入、修改或销毁任何资源
写入或修改任何引用
分配或修改除 view 函数内创建之外的任何变量。 即要注意,这意味着不能在这些函数中写入传入的或全局的变量
发出事件
调用任何非视图函数
此外,函数的前置和后置条件现在也被视为 view 上下文; 这意味着在 view 函数内部禁止的任何操作在前置或后置条件中也是不允许的。 这是为了避免在条件内修改全局或合约状态的不正当代码,它们应该只是简单地对状态属性进行断言。 特别的是由于在条件中只允许使用表达式,这意味着如果希望在条件中调用任何函数,那就必须将这些函数设为 view 函数。
■ 未报告的缺失或错误参数标签
https://github.com/onflow/cadence/pull/1717
https://github.com/onflow/cadence/pull/2013
缺失或错误参数标签的函数调用以前不会报告为 error。这种不正确的函数调用现在将会一个 error,应该通过提供预期的参数标签来修复。
举例说明:
// Contract "TestContract" deployed at address 0x1
pub contract TestContract {
pub struct TestStruct {
pub let a: Int
pub let b: String
init(first: Int, second: String) {
self.a = first
self.b = second
}
}
}
错误的程序:
TestContract.TestStruct 的初始化代码需要参数标签 first 和 second。
但是,初始化的调用为第一个参数提供了错误的参数标签,并且缺少第二个参数标签。
// Script
import TestContract from 0x1
pub fun main() {
TestContract.TestStruct(wrong: 123, "abc")
}
现在这会导致以下错误:
error: incorrect argument label
--> script:4:34
|
4 | TestContract.TestStruct(wrong: 123, "abc")
| ^^^^^ expected `first`, got `wrong`
error: missing argument label: `second`
--> script:4:46
|
4 | TestContract.TestStruct(wrong: 123, "abc")
|
正确的程序:
// Script
import TestContract from 0x1
pub fun main() {
TestContract.TestStruct(first: 123, second: "abc")
}
我们要感谢社区成员 justjoolz 报告此错误。
■ 未报告引用表达式中的错误运算符
https://github.com/onflow/cadence/pull/1661
https://github.com/onflow/cadence/pull/2010
引用表达式的语法是 &v as &T,表示将对值 v 的引用设为类型 T。
使用其他错误的运算符引用表达式(例如 as? 和 as!)在以前没有被报告为 error。
这种错误的引用表达式现在将是一个 error,应该使用 as 运算符来修复。
错误的程序:
引用表达式使用错误的运算符 as!
let number = 1
let ref = &number as! &Int
现在结果将是以下的报错:
error: cannot infer type from reference expression: requires an explicit type annotation
--> test:3:17
|
3 | let ref = &number as! &Int
|
正确的程序:
let number = 1
let ref = &numberas &Int
或者,同样的代码现在也可以写成如下:
let number = 1
let ref: &Int = &number
■在大多数情况下,标识符不能是关键字
https://github.com/onflow/cadence/pull/1937
以前,我们允许在标识符位置使用关键字(例如 continue、for 等)。
例如,解析器允许以下程序:
pub fun continue(import: Int, break: String) {...}
这导致了很多歧义,因此大多数关键字将不被允许用作名称。
在仔细审查了语言语法之后,我们目前将以下关键字指定为软限制,这意味着由于在语言中的意义有限,它们仍然可以用作标识符。 这些允许的关键字如下:
from: 只用在 import 语句中 import foo from ...
account: 用于访问修饰符 access(account) let ...
set:用于访问修饰符 pub(set) var globalMutVar = ...
all:用于访问修饰符 access(all) let ...
任何其他关键字都会在解析过程中引发错误,例如:
let break: Int = 0
// error: expected identifier after start of variable declaration, got keyword break
将合约迁移到 Stable Cadence 需要重命名与保留关键字冲突的任何标识符。
例如: let contract → let nftContract 等。
■ 修改了 U?Int(128|256) 的 toBigEndianBytes() 结果
https://github.com/onflow/cadence/pull/1917
先前 .toBigEndianBytes() 的实现是错误的,受影响的大整数类型有:
Int128
Int256
UInt128
UInt256
在较小的整数类型上调用 .toBigEndianBytes() 通常会返回适合父类型的确切字节数(左填充零)。 例如, (x: Int64).toBigEndianBytes() 返回一个 8 字节的数组。 这些较大的类型错误地返回了没有填充的可变长度字节数组,这足以存储他们的最高字节位。但这与较小的固定大小数值类型(例如 Int8 和 Int32)不一致。 为了解决这种不一致,Int128 和 UInt128 现在将返回 16 字节的数组,而 Int256 和 UInt256 返回 32 字节的数组。
let someNum: UInt128 = 123456789
let someBytes: [UInt8] = someNum.toBigEndianBytes()
// old behaviour:
// someBytes = [7, 91, 205, 21]
// new behaviour:
// someBytes = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 91, 205, 21]
如果您的合约代码需要和之前一样不做零的填充,则可以使用任意精度的 Int 和 UInt 类型来表示可变字节数组。
可以通过先转换为可变长度类型来恢复原来的效果,例如:
let someNum: UInt128 = 123456789
let someBytes: [UInt8] = UInt(someNum).toBigEndianBytes()
// someBytes = [7, 91, 205, 21]
■ 删除已弃用的密钥管理 API
https://github.com/onflow/cadence/pull/2044
去年已弃用的第一个帐户密钥管理 API 已被删除。 现在需要使用新的密钥管理 API:
更多信息可以参考 开发文档(https://developers.flow.com/cadence/language/accounts#account-keys)
■ 对资源可选绑定的修复
https://github.com/onflow/cadence/pull/2044
对资源的可选绑定(if-let 语句)现在被正确实现并修复了。 这可能会影响不正确使用的现有代码。
例如,下面的程序曾经是无效的,会报告 optR 的资源丢失错误:
resource R {}
fun asOpt(_ r: @R): @R? {
return <-r
}
fun test() {
let r <- create R()
let optR <- asOpt(<-r)
if let r2 <- optR {
destroy r2
}
}
这段代码现在将不在报错,会被视为有效的。
但是,以前为解决这个问题的所使用的部分代码,例如通过在 else 分支或 if 语句销毁资源的方式,现在将是错误的:
// invalid
fun test() {
let r <- create R()
let optR <- asOpt(<-r)
if let r2 <- optR {
destroy r2
} else {
destroy optR
}
}
即将到来的改动
以下更改可能包含在 Stable Cadence 中,但细节与决策尚未敲定。
■ Capability API 可能会被替换为一个改进的版本
https://github.com/onflow/flow/pull/798
有一个 FLIP 建议用更强大且更易用的 API 替换现有的 Capability API。私有存储域 (private) 将被移除,创建新 Capability 将更简单,撤销 Capability 也将更直接。 这不会改变 Capability 的消费 / 使用方式,只会改变它们的创建方式。 任何调用 AuthAccount::(un)link 的代码都需要更改。
■ 当资源被转移后,对资源类型的值引用可能会失效
https://github.com/onflow/flow/pull/1043
https://github.com/onflow/cadence/pull/2037
https://github.com/onflow/cadence/pull/1999
在以前即使资源被转移,对该资源的引用仍然是有效的。 换句话说引用永远存在,这可能是一个潜在的风险,可以通过引用获得 / 给予 / 持有对资源的意外访问。
通过此更改,如果在获取引用后若资源发生移动,则引用将无效。 它在第一次转移时就会失效,无论是来源还是目标。
例如:
// Create a resource.
let r <-create R()
// And take a reference.
let ref = &r as &R
// Then transfer the resource into an account.
account.save(<-r, to: /storage/r)
// Update the reference.
ref.id = 2
以前的效果:
// 这里将正常更新存储在账户的资源
ref.id = 2
现在的效果:
// 尝试更新或访问引用时,将报出以下的错
// "invalid reference: referenced resource may have been moved or destroyed"
ref.id = 2
但是,并非所有场景都可以静态检测。 例如:
pub fun test(ref: &R) {
ref.id = 2
}
在上述函数中,无法确定引用的资源是否已被移动。
因此,这种情况会在运行时进行检查,如果资源已被移动,则会发生运行时错误。
■ 必要内嵌类型可能仅限于事件
必要内嵌类型 目前支持所有类型的复合类型。
但未来的提案中可能会被限制在仅支持事件。
必要内嵌类型是一个相当高级的概念。就像接口需要实现中提供某个字段或函数一样,它也需要提供符合的内嵌类型。
这在其他编程语言中并不常见并且难以理解。 此外,必要内嵌类型的价值从未实现过。
■ 可能会引入防止重入攻击的语言支持
未来的提案可能会引入额外的语言特性,并可能改变现有的语言语义,以允许开发者自动或至少较容易地进行防重入攻击。
■ 可能会增加对外部修改的限制
当前可以对内部类型外可变值进行字段修改。未来的提案可能会限制这一点。
■ for 循环语句中变量的语义可能会改变
https://github.com/onflow/flips/pull/13
一份 FLIP 建议更改 for 循环语句中变量的效果。这将从语言中删除意外效果并减少错误的可能。
这种变化只会影响少数程序,因为只有当程序在函数闭包中获取 for 循环语句变量时,该效果才会明显。
// Capture the values of the array [1, 2, 3]
let fs: [((): Int)] = []
for x in [1, 2, 3] {
// Create a list of functions that return the array value
fs.append(fun (): Int {
return x
})
}
// Evaluate each function and gather all array values
let values: [Int] = []
for f in fs {
values.append(f())
}
以前,这个值的结果将是 [3, 3, 3],这可能会令人意外。 这是因为 x 将在每次迭代中被重新分配为当前的数组元素,导致 fs 中的每个函数都返回数组的最后一个元素。
如果提议的更改获得批准并实施,则结果将为[1, 2, 3],这会是程序作者所期望的。
相关内容
■ FT / NFT 的标准变化(尚在修改)
Fungible Token 和 Non-Fungible Token 的标准接口正在升级,以允许每个合约使用多个代币,修复原标准的一些问题,并引入社区建议的其他各种改进。
最初提案 :https://forum.onflow.org/t/streamlined-token-standards-proposal/3075
FT 修改 PR: https://github.com/onflow/flow-ft/pull/77
NFT 修改 PR: https://github.com/onflow/flow-nft/pull/126
这些修改和 FLIP 仍在进行中,因此开发者关注的预期变化尚未最终确定,但它将涉及通过事件、函数签名、资源接口一致性和其他小的更改来升级你的代币合约。会有更多示例代码即将推出。
END
什么是 Flow 福洛链?
Flow 福洛链是一个快速,去中心化,且对开发者友好的区块链,旨在为新一代游戏、娱乐应用程序提供动力的数字资产的基础。Flow 是唯一一个由始至终为消费者提供出色体验的 Layer-1 区块链团队。其团队创造的 dApp 包括:CryptoKitties、Dapper Wallets、NBA Top shot。
CrytoKitties 于 2017 年推出时便快速成为加密市场最受欢迎的 dApp,因其成功而导致以太坊堵塞。在 Flow 上运营的 NBA Top shot 也已成为增长最快的 dApp,在公开发布后的 6 个月创造了 7 亿美金销量。正因为 Flow 公链的可扩展性和消费者友好的体验,让这一切成为可能。目前有 1000 多个项目正在 Flow 链上筹备中,我们期待看到一个伟大的生态系统蓬勃发展。
关于 Dapper Labs
Dapper Labs 是一家位于加拿大的全球顶尖区块链服务商,在 2017 年年底通过 CryptoKitties 收藏游戏成功进入⽤户视野,并且因为加密猫的爆⽕导致以太坊拥堵,从而推出 Flow 公链以及全新的开发语言—— Cadence,旨在吸引更多的开发者在 Flow 上开发应⽤。
Flow 的合作伙伴们:
我们欢迎越来越多的小伙伴加入 Flow 星球,为星球增添色彩!
Flow 官网:https://zh.onflow.org/
Flow 论坛: https://forum.onflow.org/
Flow Discord:
https://discord.com/invite/flow
Flow CN Telegram: https://t.me/flow_zh
Flow B 站:https://space.bilibili.com/1002168058
Flow 微博:
https://weibo.com/7610419699
Flow CSDN:
https://blog.csdn.net/weixin_57551966?spm=1010.2135.3001.5343
扫码添加 Flow 官方账号微信号,加入 Flow 生态群
微信号 : FlowChainOfficial
【免责声明】市场有风险,投资需谨慎。本文不构成投资建议,用户应考虑本文中的任何意见、观点或结论是否符合其特定状况。据此投资,责任自负。