Move QA
Move 账户模型
Move 语言的账户模型与以太坊的账户模型有一些不同。Move 是为 Libra 区块链(后来更名为 Diem)设计的编程语言,它的账户模型特别强调资源的安全性和类型安全。以下是 Move 账户模型的一些关键特性:
-
资源类型:Move 语言引入了一种名为 "资源" 的特殊类型,这是一种只能按照预定规则移动的数据结构。资源不能被复制或意外丢弃,这确保了资源的保存和转移都是安全的。
-
账户作为资源容器:在 Move 中,每个账户都可以被看作是一个存储资源的容器。账户可以拥有多种资源,并且资源的类型和数量可以随着时间变化。例如,一个账户可以拥有多种不同类型的代币资源。
-
模块和脚本:Move 代码被组织成模块和脚本。模块是持久存储在区块链上的,它们定义了资源类型和与资源交互的函数。脚本是一次性的交易,它们调用模块中的函数来执行操作。
-
能力和访问控制:Move 使用能力(capabilities)和访问控制列表来限制对资源的访问。这意味着只有拥有正确能力的账户才能对资源执行特定的操作。
-
账户地址:Move 中的每个账户都有一个唯一的地址,这个地址用于标识资源的位置。账户地址通常是由用户的公钥派生而来。
-
交易身份验证:当一个交易被发送到区块链时,它必须由发起交易的账户的私钥签名。这确保了只有账户的所有者才能发起影响其资源的交易。
-
智能合约部署:在 Move 中,智能合约(模块)被部署到一个特定的账户地址下。模块一旦部署,就不能被修改,这提高了代码的不可变性和安全性。
-
资源的创建和销毁:资源只能通过定义在模块中的构造函数来创建,并且只能通过定义在模块中的析构函数来销毁。这种限制确保了资源的生命周期可以被可靠地管理。
Move 语言的设计目标是为了提供一个安全、灵活且可扩展的框架,以支持各种资产的代表和处理,包括加密货币、代币、NFTs 等。它的账户和资源模型是为了减少智能合约中的错误和安全漏洞,特别是那些可能导致资产丢失或被盗的漏洞。
Move 子类型化(Subtyping)
子类型化(Subtyping)是编程语言类型系统中的一个概念,它描述了一种类型关系,其中一个类型(子类型)可以被认为是另一个类型(超类型)的特化。这种关系通常用于支持多态性,即相同的代码可以操作不同类型的对象。
在子类型化的情况下,如果类型 A 是类型 B 的子类型,那么所有需要 B 类型的地方都可以安全地使用 A 类型的对象。这是因为 A 类型的对象不仅具有 B 类型的所有特性,而且可能还有更多。这种关系通常用“是一个(is-a)”的术语来描述,如“所有狗都是动物”,因此狗类型是动物类型的子类型。
在面向对象编程中,子类型化通常通过继承来实现。如果一个类 Dog 继承自另一个类 Animal,那么 Dog 可以被认为是 Animal 的子类型。
子类型化的关键特性包括:
- 替代原则(Liskov Substitution Principle):如果
S是T的子类型,那么任何T类型的对象都可以被S类型的对象替换,而不会影响程序的正 确性。 - 继承:子类型通常会继承超类型的属性和方法。
- 多态:允许使用超类型的变量或参数来引用子类型的对象,从而使得相同的代码可以处理不同的数据类型。
子类型化是静态类型语言中常见的概念,如 Java、C# 和 TypeScript。它允许开发者编写更通用和可重用的代码,同时保持类型安全。
在一些动态类型语言中,虽然没有明确的类型声明,但也存在类似的概念,称为鸭子类型(duck typing)。在鸭子类型中,如果一个对象可以执行所需的方法,它就可以被视为特定的类型,不管它实际上是什么类型。这也是一种形式的多态性,但它是在运行时而不是在编译时确定的。
需要注意的是,不是所有编程语言都支持子类型化,尤其是那些没有类或类型继承概念的语言。
Move copy 变量 copy s 是啥意思?
address 0x42 {
module example {
struct S has copy, drop { f: u64, s: vector<u8> }
fun always_true(): bool {
let s = S { f: 0, s: b"" };
// 括号不是必需的,但为了清楚起见在此示例中添加了括号
(copy s) == s
}
fun always_false(): bool {
let s = S { f: 0, s: b"" };
// 括号不是必需的,但为了清楚起见在此示例中添加了括号
(copy s) != s
}
}
}
在 Move 语言中,copy 关键字用于显式地复制一个值类型的实例。在 Move 中,值类型可以有 copy、drop 和 store 等能力(abilities),这些能力决定了类型的值可以如何被操作。
copy能力允许类型的值被复制。drop能力允许类型的值被丢弃。store能力允许类型的值被存储在全局存储中。
在你提供的代码示例中,struct S 被声明具有 copy 和 drop 能力,这意味着 S 的实例可以被复制和丢弃。
let s = S { f: 0, s: b"" };
(copy s) == s
在这个表达式中,copy s 创建了 s 的一个副本。由于 S 有 copy 能力,这是允许的操作。然后,该表达式检查复制的 s 是否等同于原始的 s。在 Move 中,结构体的相等性是基于它们字段的相等性来判定的,因此这个比较结果是 true,因为 s 和它的副本具有相同的字段值。
相反,always_false 函数中的这个表达式:
(copy s) != s
检查复制的 s 是否不等于原始的 s。由于 s 和它的副本具有相同的字段值,这个比较结果是 false。
这里需要注意的是,copy 关键字在 Move 中是显式的,这是 Move 语言设计中的一个特点,它强调资源的精确管理。在很多其他编程语言中,复制通常是隐式进行的,但 Move 要求开发者显式指出何时进行复制操作,以避免无意中复制可能代表重要资源的值。
Move vector::borrow
vector::borrow 为什么有这个 api?
在 Move 语言中,vector::borrow 函数提供了一种安全的方式来借用向量(vector)中的一个元素的不可变引用。这个 API 是必要的,因为它允许程序员访问向量中的单个元素,而不需要获取整个向量的所有权或者复制整个向量。
vector::borrow 函数的签名如下:
public fun borrow<Element>(v: &vector<Element>, i: u64): &Element
这个函数接受两个参数:一个对向量的不可变引用和一个索引。它返回一个对向量中指定索引处元素的不可变引用。如果索引超出了向量的界限,这个函数会触发运行时错误。
这个 API 的存在有几个原因:
-
效率:如果你只需要访问向量中的一个元素,那么复制整个向量显然是低效的。
vector::borrow允许你只获取对你需要的元素的引用。 -
安全性:Move 语言的设计注重于安全性,特别是在处理资源时。通过返回元素的引用而不是值本身,
vector::borrow确保了在借用期间原始向量不能被修改或销毁,从而防止了悬垂引用(dangling references)和其他潜在的安全问题。 -
并发性:虽然当前 Move 语言主要用于区块链上的智能合约,其中并发性不是主要问题,但 Move 的设计允许未来可能的并发执行。在这种情况下,
vector::borrow通过提供对单个元素的不可变引用,而不是对整个向量的可变引用,有助于避免数据竞争。 -
模式匹配:在 Move 中,模式匹配(pattern matching)还不是语言的一部分,因此不能像在某些其他语言中那样直接提取向量的元素。
vector::borrow提供了一种机制来访问向量中的元素,而不需要改变 向量的状态或结构。
综上所述,vector::borrow API 是 Move 语言中处理向量元素时的一个重要工具,它使得以一种安全、高效的方式访问向量的单个元素成为可能。
为什么不直接使用&?
在某些编程语言中,你可以直接使用 & 操作符来获取对某个数据结构中元素的引用。然而,在 Move 语言中,向量(vector)是通过一个模块提供的抽象数据类型,而不是语言内置的一部分。这意味着对向量中元素的操作需要通过该模块提供的 API 来完成,而不是通过直接的语言构造。
vector::borrow 是 Move 标准库中 Vector 模块提供的一个函数,它允许安全地借用向量中的元素。这种设计有几个原因:
-
封装:Move 的
Vector模块封装了向量的内部表示和操作。这种封装允许Vector模块的实现随着时间的推移而变化,而不会破坏依赖于该模块的代码。 -
安全性:
vector::borrow函数在尝试访问超出向量界限的索引时会导致运行时错误。这是一种故意的设计选择,它强制执行了边界检查,从而防止了潜在的内存安全问题。 -
一致性:Move 的设计者可能想要提供一组一致的 API 来操作
Vector类型,这样用户就不必记住特定的语言构造来处理向量,而是可以记住一组函数接口。 -
抽象层次:通过使用 API 函数而不是语言内置的操作符,Move 的设计者可以提供一个更高层次的抽象。这意味着向量的实现可以优化,而不需要暴露给用户复杂的内部细节。
如果 Move 允许直接使用 & 操作符来借用向量中的元素,那么这将要求向量的实现细节必须是语言的一部分,这可能会限制向量实现的灵活性,并且可能导致语言更加复杂。通过将这些操作封装在 API 调用中,Move 语言可以保持简洁,并将复杂性隐藏在库的实现中。
Move entry
在 Move 语言中,entry 是一个关键字,用于标记函数为入口点。入口点是可以被外部账户直接调用的函数,它们是智能合约与外部世界交互的接口。这个概念在 Move 语言中非常重要,因为 Move 旨在为区块链环境提供安全和可靠的智能合约编程模型。
在 Move 中,不是所有的函数都可以被外部账户调用。默认情况下,函数是私有的,只能被定义它们的模块内部的其他函数调用。如果你想允许外部交易或其他模块调用某个函数,你需要使用 public 关键字将函数声明为公共的。进一步地,如果你希望这个公共函数可以被区块链上的交易直接调用,你需要在 public 关键字之后添加 entry 关键字。
这是一个 Move 函数声明为入口点的例子:
public entry fun my_entry_function(arg: u64) {
// 函数体
}
在这个例子中,my_entry_function 被声明为一个入口点,这意味着它可以被外部的交易调用。
entry 关键字的使用提供了以下好处:
-
安全性:明确指定哪些函数可以作为交易的入口点,有助于智能合约的作者控制对合约状态的修改和访问,减少了错误或恶意调用的风险。
-
清晰性:通过查看函数签名,开发者和审计者可以快速识别哪些函数是设计为交易的入口点,这有助于理解合约的行为和交互模式。
-
模块化:
entry关键字强制模块化设计,因为它要求开发者思考哪些功能应该对外部世界暴露,哪些应该保持内部私有。
总的来说,entry 关键字是 Move 语言中用于控制智能合约行为和提高安全性的众多关键字之一。通过这样的关键字,Move 语言旨在提供一个更加安全和可预测的编程环境,特别是在关系到资产和其他敏感操作的区块链应用中。