Share Dialog
Share Dialog
Subscribe to 0xturbofish.eth
Subscribe to 0xturbofish.eth
<100 subscribers
<100 subscribers
对于Move语言本身就不过多介绍了,脱身于原Facebook Diem(Libra)项目,之后衍生出了不少基于Move开发的公链项目,感兴趣小伙伴可以去研究一下,初看到这个list也超出了我的预期。所以感兴趣尤其是有一定rust的基础的朋友不要错过!
Move目前的开发doc已经很完善了,这里主要是把自己阅读move book里的一些note记录下来,当作cheetsheet方便开发过程中查找。原文档的顺序不太适合有一定经验的开发者阅读,简单调整了下顺序。这里主要以aptos move开发为例。
原move book是从基本语法讲起,然后过度到类型介绍,流程控制,结构和多态,账户模型最后到项目模型和测试。
这里大体是反过来的顺序,从顶向下来看。
Move项目结构整体类似Rust项目结构,这里是aptos framwork项目结构供参考。
a_move_package
├── Move.toml (required)
├── sources (required)
├── examples (optional, test & dev mode)
├── scripts (optional)
├── doc_templates (optional)
└── tests (optional, test mode)
其中sources以及Move.toml都比较好理解,值得注意的是Move.toml中会辅助定义named address,这个会在部署合约的时候用到。
重点关注一下Move.toml中,各个地址之间的关系,考虑两种情况
本module作为最终部署的合约入口
本module作为中间层,被其他module调用
[package]
name = "ExamplePkg"
...
[addresses]
my_deployment_addr = "0x007"
// other deployment addr for dependency
std = "0x01"
同样参考aptos framework的coin例子,这里我们一个非常简单的智能合约(move中叫module)就可以这样完成。
module my_deployment_addr::message {
use std::string;
use std::error;
...
struct MessageHolder has key {
message: string::String,
}
public entry fun set_message(account: signer, message_bytes: vector<u8>) {...}
public fun get_message(addr: address): string::String acquires MessageHolder {...}
}
这里为了便于理解,把本合约named address叫做my_deployment_addr。部署时候,需要以这个地址部署,部署完成后。合约位于my_deployment_addr的module空间下,这里实际部署的地址为0x007。同时该合约还调用了位于0x001地址下的另一个合约。
需要的注意的是,所有的地址在编译时候需要会被翻译为实际地址,否则在部署时无法找到合约需要部署的地址空间。同样这里会有两种方式制定 1/ 编译器CLI传入 2/ Move.toml中制定。
通常软件开发的习惯是config统一定义,首先module作为中间层时,我们同样可以制定地址。比如下面的std = "0x01",是非常明确地址的标准库。但是对于我们自定义的很多中间层而言,并不希望在这里指定地址。于是出现了另一种wildcard指定方式。
[package]
name = "ExamplePkg"
...
[addresses]
my_deployment_addr = "_"
// other deployment addr for dependency
std = "0x01"
这里用_替代,使得其他依赖此package的可以自定义相关地址。
简单来说,move项目结构比较简单。主要容易混淆的地方在与部署地址以及module依赖关系中,地址的制定和传递。
熟悉以太部署的小伙伴可能很自然会有一个反应,部署地址有了,那么合约部署到了哪里?谁又有权限呢?简单来讲,不同于以太部署会生成对应的合约地址,对应代码bytecode位于新生成的合约地址空间。Move的合约会被保存在部署地址的空间下,这里在下个部分仔细讲。
Move官方文档的对于账户空间的描述简单易懂,伪代码如下
struct GlobalStorage {
resources: Map<(address, ResourceType), ResourceValue>
modules: Map<(address, ModuleName), ModuleBytecode>
}
描述为,账户可访问空间包括且仅包括两部分,账户无法访问这两部分之外的账户资源(这里的资源更偏操作系统定义,包括文件系统,网络等)
文档中对Move Program的描述是,通过Move Program实现对于global storage的读写。
我们来看一下两部分,其中resources表示该账户的空间的数据部分,而modules表示该账户空间的代码部分。值得注意的是,这里账户的数据空间(resources)以及代码空间(modules)的Map键值结构类似,都是地址以及另一个相关Name/Type的tuple。这样就意味着:
对于同一个地址,同一个资源类型只会存储一个值。
对于同一个地址,同名的module,也只能存在一个。
这样的描述仍然比较抽象,回到上面项目结构中的例子。项目结构中,地址0x007部署了,module名为message的代码,该代码描述了MessageHolder字符以及提供了getter&setter借口,我们来看一下部署完成之后的代码和数据空间。请注意,前面提到了,地址0x007将代码部署在其本地空间中,所以下面我们讨论的是0x007的空间而非其他地址(比如新生成的地址)。
其代码空间看起来是:
(0x007, message) : {
"module_description": {...}
"byte_code": {...}
}
一个以(0x007, message)为key,module abi(description)以及bytecode为值的k-v pair出现在代码空间中。
如果我们不调用该module的setter结构,数据空间当中当然没有数据,考虑我们调用0x007::message::set_messageset了这个值Hello Move from 0x007,此时的数据空间将会变成
(0x007, 0x007::message::MessageHolder) : {
"message" : "Hello Move from 0x007"
}
这时如果另一个账户0x008,同样调用了0x007::message::set_message设置为Hello Move from 0x008会发生什么呢?
首先0x008对0x007的账户以及数据空间并没有影响
然后0x008并没有进行任何部署module的操作,所以其代码空间也没有影响
最后0x008的数据空间,这个key发生了变化(0x008, 0x007::message::MessageHolder)
其数据空间将会变为
(0x008, 0x007::message::MessageHolder) : {
"message" : "Hello Move from 0x008"
}
简单小结一下,本来是想写一个cheetsheet,主要是总结一下各种易忘点,比如各种primitive type的特性,copy&drop的特性,reference和global storage borrow等等。慢慢抛开项目以及账户结构,这些写的很空泛。于是这一篇主要以实例的方式理解了一下move合约的大框架,希望对于不是开发者小伙伴也能有些帮助。后续主要是个人读技术文档以及编程过程中的notes,当作一个工具箱使用,大家happy coding = )也欢迎一起学习指正。
对于Move语言本身就不过多介绍了,脱身于原Facebook Diem(Libra)项目,之后衍生出了不少基于Move开发的公链项目,感兴趣小伙伴可以去研究一下,初看到这个list也超出了我的预期。所以感兴趣尤其是有一定rust的基础的朋友不要错过!
Move目前的开发doc已经很完善了,这里主要是把自己阅读move book里的一些note记录下来,当作cheetsheet方便开发过程中查找。原文档的顺序不太适合有一定经验的开发者阅读,简单调整了下顺序。这里主要以aptos move开发为例。
原move book是从基本语法讲起,然后过度到类型介绍,流程控制,结构和多态,账户模型最后到项目模型和测试。
这里大体是反过来的顺序,从顶向下来看。
Move项目结构整体类似Rust项目结构,这里是aptos framwork项目结构供参考。
a_move_package
├── Move.toml (required)
├── sources (required)
├── examples (optional, test & dev mode)
├── scripts (optional)
├── doc_templates (optional)
└── tests (optional, test mode)
其中sources以及Move.toml都比较好理解,值得注意的是Move.toml中会辅助定义named address,这个会在部署合约的时候用到。
重点关注一下Move.toml中,各个地址之间的关系,考虑两种情况
本module作为最终部署的合约入口
本module作为中间层,被其他module调用
[package]
name = "ExamplePkg"
...
[addresses]
my_deployment_addr = "0x007"
// other deployment addr for dependency
std = "0x01"
同样参考aptos framework的coin例子,这里我们一个非常简单的智能合约(move中叫module)就可以这样完成。
module my_deployment_addr::message {
use std::string;
use std::error;
...
struct MessageHolder has key {
message: string::String,
}
public entry fun set_message(account: signer, message_bytes: vector<u8>) {...}
public fun get_message(addr: address): string::String acquires MessageHolder {...}
}
这里为了便于理解,把本合约named address叫做my_deployment_addr。部署时候,需要以这个地址部署,部署完成后。合约位于my_deployment_addr的module空间下,这里实际部署的地址为0x007。同时该合约还调用了位于0x001地址下的另一个合约。
需要的注意的是,所有的地址在编译时候需要会被翻译为实际地址,否则在部署时无法找到合约需要部署的地址空间。同样这里会有两种方式制定 1/ 编译器CLI传入 2/ Move.toml中制定。
通常软件开发的习惯是config统一定义,首先module作为中间层时,我们同样可以制定地址。比如下面的std = "0x01",是非常明确地址的标准库。但是对于我们自定义的很多中间层而言,并不希望在这里指定地址。于是出现了另一种wildcard指定方式。
[package]
name = "ExamplePkg"
...
[addresses]
my_deployment_addr = "_"
// other deployment addr for dependency
std = "0x01"
这里用_替代,使得其他依赖此package的可以自定义相关地址。
简单来说,move项目结构比较简单。主要容易混淆的地方在与部署地址以及module依赖关系中,地址的制定和传递。
熟悉以太部署的小伙伴可能很自然会有一个反应,部署地址有了,那么合约部署到了哪里?谁又有权限呢?简单来讲,不同于以太部署会生成对应的合约地址,对应代码bytecode位于新生成的合约地址空间。Move的合约会被保存在部署地址的空间下,这里在下个部分仔细讲。
Move官方文档的对于账户空间的描述简单易懂,伪代码如下
struct GlobalStorage {
resources: Map<(address, ResourceType), ResourceValue>
modules: Map<(address, ModuleName), ModuleBytecode>
}
描述为,账户可访问空间包括且仅包括两部分,账户无法访问这两部分之外的账户资源(这里的资源更偏操作系统定义,包括文件系统,网络等)
文档中对Move Program的描述是,通过Move Program实现对于global storage的读写。
我们来看一下两部分,其中resources表示该账户的空间的数据部分,而modules表示该账户空间的代码部分。值得注意的是,这里账户的数据空间(resources)以及代码空间(modules)的Map键值结构类似,都是地址以及另一个相关Name/Type的tuple。这样就意味着:
对于同一个地址,同一个资源类型只会存储一个值。
对于同一个地址,同名的module,也只能存在一个。
这样的描述仍然比较抽象,回到上面项目结构中的例子。项目结构中,地址0x007部署了,module名为message的代码,该代码描述了MessageHolder字符以及提供了getter&setter借口,我们来看一下部署完成之后的代码和数据空间。请注意,前面提到了,地址0x007将代码部署在其本地空间中,所以下面我们讨论的是0x007的空间而非其他地址(比如新生成的地址)。
其代码空间看起来是:
(0x007, message) : {
"module_description": {...}
"byte_code": {...}
}
一个以(0x007, message)为key,module abi(description)以及bytecode为值的k-v pair出现在代码空间中。
如果我们不调用该module的setter结构,数据空间当中当然没有数据,考虑我们调用0x007::message::set_messageset了这个值Hello Move from 0x007,此时的数据空间将会变成
(0x007, 0x007::message::MessageHolder) : {
"message" : "Hello Move from 0x007"
}
这时如果另一个账户0x008,同样调用了0x007::message::set_message设置为Hello Move from 0x008会发生什么呢?
首先0x008对0x007的账户以及数据空间并没有影响
然后0x008并没有进行任何部署module的操作,所以其代码空间也没有影响
最后0x008的数据空间,这个key发生了变化(0x008, 0x007::message::MessageHolder)
其数据空间将会变为
(0x008, 0x007::message::MessageHolder) : {
"message" : "Hello Move from 0x008"
}
简单小结一下,本来是想写一个cheetsheet,主要是总结一下各种易忘点,比如各种primitive type的特性,copy&drop的特性,reference和global storage borrow等等。慢慢抛开项目以及账户结构,这些写的很空泛。于是这一篇主要以实例的方式理解了一下move合约的大框架,希望对于不是开发者小伙伴也能有些帮助。后续主要是个人读技术文档以及编程过程中的notes,当作一个工具箱使用,大家happy coding = )也欢迎一起学习指正。
No activity yet