工程师们经常使用 "所有权 "这个术语来比喻跟踪哪段代码负责管理某种数据结构或系统资源。这种隐喻在编程环境中最为常见,在这种环境中,内存管理并没有完全从程序员那里抽象出来,说代码 "拥有 "一个对象就是说代码必须管理和释放分配给该对象的内存。
虽然现有的编程环境可以用来跟踪资产的所有权,但它们最典型的使用场景是反映所有权而不是直接定义所有权。像Flow这样的公共区块链是独一无二的,因为它们被明确设计为管理具有真正稀缺性和完全访问控制的数字资产的所有权。比特币或Flow等公共区块链上的数字资产的行为与实物资产一样:它们不能被复制或伪造,只能被移动。
随着区块链的发展,代表所有权的机制已经改变。比特币是使用由 "未使用的交易产出 "或UTXO定义的所有权模型建立的。虽然效率很高,但UTXO模型很复杂,会产生一些不寻常的边缘情况,所以以太坊采用了一个更灵活的账本模型。不幸的是,以太坊模型的灵活性使其难以编写安全和可靠的智能合约,导致黑客攻击,使加密货币社区损失了1亿美元以上的资金。
介绍Cadence,第一个面向资源的高级编程语言
去年,Flow团队在对更好的智能合约语言进行学术研究后,正在调查线性类型在区块链背景下的使用。就在同一时间,Libra团队发布了他们的初始公告,包括MoveVM的技术细节。
Libra团队为Move定义了一个新的编程模型,该模型围绕着受线性类型启发的新所有权模型:资源。资源是一种在编程语言中直接代表资产所有权和加密数字资产属性的新方式。来自Move的介绍。
Move的主要特点是能够定义自定义资源类型。资源类型用于编码具有丰富可编程性的安全数字资产。
我们被面向资源的编程的力量所震撼,它是我们为Flow开发的智能合约编程语言Cadence的决定性特征之一。
作为第一个面向资源的高级编程语言,Cadence有一个舒适的、符合人体工程学的语法,使其非常容易阅读。它使用一个强大的静态类型系统来减少运行时的错误,并允许所有的方法、接口和事务包括前、后条件来执行预期的行为。我们认为这将导致一种语言更容易学习,更容易审计,并最终比目前的任何替代品更有成效。
你可以在Flow Playground上试验Cadence,今天可以在play.onflow.org上使用。
面向资源的编程是如何工作的?
资源比EVM或WASM释放出更丰富的可组合性选项,是数字资产的完美选择。将某样东西标记为 "资源 "告诉编程环境,这个数据结构代表了某种有形的价值,所有与该数据结构互动的代码都需要遵循一系列特殊的规则,以保持该数据结构的价值。
那么,这些规则是什么?
- 每个资源在任何时候都精确地存在于一个地方。资源不能被复制或意外删除,无论是通过编程错误还是恶意代码。
- 资源的所有权是由它的存储地点决定的。不存在需要查阅的中央账簿来确定所有权。
- 对资源上的方法的访问只限于所有者。例如,只有CryptoKitty的主人可以启动繁殖操作,导致一个新的Kitty诞生。
资源对象的特殊地位仅由编译器执行是不够的。当代码在链上实际执行时,这些规则必须被强制执行;对于攻击者来说,使用被破坏的编译器副本来绕过保持资源安全的规则是非常容易的。
然而!如果你确实适当地执行了这些规则,你可以允许网络中最重要的资产--本地令牌--安全地存储在由用户提交的代码控制的数据结构内。强大的!
给我看一个例子!
思考资源的最简单方法是通过一个使用不可伪造代币(NFT)的例子,如CryptoKitty。每个CryptoKitty都是不可分割的,不可复制的,并且可以有一个直接所有者,这与资源编程结构直接匹配。
在以太坊这样的账本模式中,所有的CryptoKitties都作为一个巨大的列表存储在一个智能合约中。每个Kitty的所有权是通过在中央账本中存储每个所有者的账户ID来追踪的,改变Kitty所有权的唯一方法是联系中央账本,要求它更新与该Kitty相关的账户ID。
contract KittyLedger {
\tstruct Kitty {}\n
\tpriv let kitties: {Int: Kitty}\n
\tfun transfer(kittyId: Int, newOwner: AccountId) {
\t\tif (msg.sender == kitties[kittyId].owner) {
\t\t\tkitties[kittyId].owner = newOwner
\t\t}
\t}
}\n
transaction(signer: Account) {
\t// tells the central ledger to assign ownership of
\t// myKittyId to a different account
\tcentralKittyLedger.transfer(myKittyId, receiverAccountId)
}
在资源模型中,Kitty本身被表示为一个资源对象,它被直接存储在拥有它的账户中。就像在物理世界中一样,所有权是由占有表示的。你不需要在中央账簿中查看你是否拥有某样东西,你要么把它储存在你的账户中,要么没有。如果你拥有它,你可以转让它或以其他方式控制它,如果你不拥有它,就没有办法捕获或改变它。
contract CryptoKitties {
\t// Accounts store a collection in their account storage
\tresource KittyCollection {
\t\t// Each collection has functions to
\t\t// move stored resources in and out
\t\tfun withdraw(kittyId: int): CryptoKitty
\t\tfun deposit(kitty: CryptoKitty)
\t}\n
\t// The resource objects that can be stored in the collection
\tresource CryptoKitty {}
}\n
transaction(signer: Account) {
\t// Removes the Kitty from signer's collection, and stores it
\t// temporarily on the stack.
\tlet theKitty <- signer.kittyCollection.withdraw(kittyId: myKittyId)\n
\t// Moves the Kitty into the receiver's account
\tlet receiver = getAccount(receiverAccountId)
\treceiver.kittyCollection.deposit(kitty: <-theKitty)
}
注意:为了保持专注于账本和直接所有权模型之间的差异,上面的两个例子都忽略了访问控制、定义每个变量等问题,以及其他活体代码需要担心的因素。
为什么资源很重要
除了包括管理所有权的抽象的明显优势外,使用资源还有其他一些次要的好处,每一个好处本身都是相当重要的。
州租
可扩展的智能合约平台需要某种方式来收取 "状态租金",以便存储在区块链上的数据要么被支付,要么从工作集中删除。
有了分类账模型,就很难知道谁应该支付这个租金。例如,CryptoKitties合约代表了数以万计的玩家,拥有近200万只Kitty和超过111MB的链上数据。以太坊没有提供公平地向所有这些Kitty所有者收取租金的方法。
通过资源类型使用直接所有权模式,每个小鹰将被存储在其所有者的账户内,与该人的其他资产一起。谁需要支付该存储的责任是明确的。更重要的是,个人用户(在其客户端软件的协助下)可以将未使用的资产归档,以减少他们的成本并降低网络的负载。
灵活的所有权
使用所有权的分类账模型限制了可用的所有者关系的种类。例如,ERC-721为NFT定义了一个所有权模型,假设只有以太坊地址可以拥有一个NFT。然而,资产本身拥有其他资产的想法(如CryptoKitty拥有一副漂亮的太阳镜)在某些用例中非常有趣,并需要创建一个新的规范(ERC-998)。ERC-998非常强大,但它也比ERC-721复杂得多。正确地实施它是非常困难的,而且将其功能追溯到现有的ERC-721资产上实际上是不可能的。
直接所有权模型允许任何使用资源类型建模的资产安全地存储在系统中的任何地方,包括在适当的情况下,"在 "其他资产的内部。所有的安全和价值保证都可以由运行时系统来维护,同时为开发者释放出创造性的灵活性,而不会有过度的复杂性。
基于能力的安全
资源类型提供了实现基于能力的安全模型的 "能力 "概念所需的所有保证。能力是定义安全系统的强大机制,可以使遵守最小特权原则(安全系统中常见的最佳实践)变得更加容易。
基于能力的安全模型通常被认为更容易推理(这增强了安全性),同时允许更大的灵活性。
消除重入错误
以太坊历史上最有名的智能合约漏洞是由于重入问题造成的,Solidity开发者需要不断警惕引入易受重入攻击的逻辑流。
幸运的是,定义在资源对象上的方法不会成为任何重入性漏洞的受害者。
这似乎是一个大胆的主张!然而,这是由资源的定义方式自然而然产生的。每个资源都有一个所有者,而且只有资源的所有者可以调用它的方法。如果一个资源方法在 "堆栈 "中,我们知道该对象的单一所有权引用已经被使用了;我们从该方法中调用的任何代码--无论多么间接--都不可能获得该对象的第二个引用来进行可重入的方法调用。
当然,直接使用全局共享状态(绕过资源对象的使用)仍然可以创建易受重入错误影响的代码。这就是为什么Cadence的习惯性风格是使用资源来处理所有的共享状态;接受资源的智能合约作者不需要再考虑可重入性错误了。
关于资源的更多信息
要深入挖掘资源和以资源为导向的编程,你可以。
- 阅读Move技术文件和开发者文档
- 阅读Flow开发者文档,其中包括对Cadence的介绍。
- 在Flow Playground上学习Cadence编程语言