# Move Cheatsheet-0 项目结构&账户结构

By [0xturbofish.eth](https://paragraph.com/@0xturbofish) · 2022-07-25

---

Overview
--------

对于Move语言本身就不过多介绍了，脱身于原Facebook Diem（Libra）项目，之后衍生出了不少基于Move开发的[公链项目](https://github.com/MystenLabs/awesome-move#move-powered-blockchains)，感兴趣小伙伴可以去研究一下，初看到这个list也超出了我的预期。所以感兴趣尤其是有一定rust的基础的朋友不要错过！

Move目前的开发doc已经很完善了，这里主要是把自己阅读[move book](https://move-language.github.io/move/introduction.html)里的一些note记录下来，当作cheetsheet方便开发过程中查找。原文档的顺序不太适合有一定经验的开发者阅读，简单调整了下顺序。这里主要以aptos move开发为例。

### CheatSheet

原move book是从基本语法讲起，然后过度到类型介绍，流程控制，结构和多态，账户模型最后到项目模型和测试。

这里大体是反过来的顺序，从顶向下来看。

### 项目结构

Move项目结构整体类似Rust项目结构，这里是[aptos framwork项目结构](https://github.com/aptos-labs/aptos-core/tree/main/aptos-move/framework/aptos-framework)供参考。

    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调用
    

#### 本module作为最终部署的合约入口

    [package]
    name = "ExamplePkg"
    ...
    [addresses]
    my_deployment_addr = "0x007"
    // other deployment addr for dependency
    std = "0x01"
    

同样参考[aptos framework的coin例子](https://github.com/aptos-labs/aptos-core/blob/main/aptos-move/framework/aptos-framework/sources/coin.move#L3-L4)，这里我们一个非常简单的智能合约（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中制定。

#### 本module作为中间层

通常软件开发的习惯是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_message`set了这个值`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"
    }
    

Summary
-------

简单小结一下，本来是想写一个cheetsheet，主要是总结一下各种易忘点，比如各种primitive type的特性，copy&drop的特性，reference和global storage borrow等等。慢慢抛开项目以及账户结构，这些写的很空泛。于是这一篇主要以实例的方式理解了一下move合约的大框架，希望对于不是开发者小伙伴也能有些帮助。后续主要是个人读技术文档以及编程过程中的notes，当作一个工具箱使用，大家happy coding = ）也欢迎一起学习指正。

---

*Originally published on [0xturbofish.eth](https://paragraph.com/@0xturbofish/move-cheatsheet-0)*
