查看原文
其他

操作资源 | Move dApp极速入门(三)

NonceGeekDAO 李大狗Leeduckgo 2022-08-08

Hello Move | Move dApp极速入门(一)

第一个 Move dApp | Move dApp极速入门(二)

作者:李大狗、陈俊峰

本系列将以 Starcoin 为例,讲解 Move 语言以及 Move dApp 的开发,及其背后的计算机原理。

本系列的全文更新中,见:

https://github.com/WeLightProject/Web3-dApp-Camp/tree/main/move-dapp

同步的打卡任务:

https://github.com/WeLightProject/Web3-dApp-Camp/discussions/categories/projects-others

「资源」是 Move 语言中最关键的概念。本篇将以 Library 图书馆 Demo 为例,讲解在 Rust 下是如何操作资源的。

0x01 源码下载

本文的 Demo 源码包含 smart contractreact dAppscript.sh三部分,资源链接如下:

https://github.com/WeLightProject/Web3-dApp-Camp/tree/main/move-dapp/my-library

0x02 Types with Abilities

https://move-book.com/advanced-topics/types-with-abilities.html#types-with-abilities

https://move-book.com/cn/advanced-topics/types-with-abilities.html

Move 的类型系统非常灵活,每种类型都可以定义四种能力(abilities)。它们决定了类型的值是否可以被「使用、丢弃和存储」。

这四种 abilities 能力分别是: Copy, Drop, Store 和 Key。

它们的功能分别是:

  • Copy - 值可以被复制
  • Drop - 在作用域(Scope)结束时值可以被丢弃
  • Key - 值可以作为键值(Key)被「全局存储操作( global storage operations)」进行访问
  • Store - 值可以被 存储 到全局状态。

在上一篇中,我们已经初步接触到了 Abilities。在本实例中,我们将进一步地通过 Play with Abilities 掌握其原理。

0x03 Abilities 的语法

基本类型和内建类型的 abilities 是预先定义好的并且不可改变: integers, vector, addresses 和 boolean 类型的值先天具有 copy、drop 和 store ability。

然而,结构体的 ability 可以按照下面的语法进行添加:

struct NAME has ABILITY [, ABILITY] { [FIELDS] }

一个图书馆的Struct例子:

module Library {
    
    // each ability has matching keyword
    // multiple abilities are listed with comma
    struct Book has store, copy, drop {
        year: u64
    }

    // single ability is also possible
    struct Storage has key {
        books: vector<Book>
    }

    // this one has no abilities 
    struct Empty {}
}

0x04 Library 合约实践

合约源码见:

https://github.com/WeLightProject/Web3-dApp-Camp/blob/main/move-dapp/my-library/sources/MyLibrary.move

对应的脚本合集如下(脚本中的路径、地址等信息需自行调整):

# deploy
dev deploy [path to blob] -s [addr] -b
dev deploy /Users/cjf/Documents/bc/Web3-dApp-Camp/move-dapp/my-library/release/my_library.v0.0.3.blob -s 0x07ffe973c72356c25e623e2470172a69 -b
# call function init library
account execute-function --function 0x07Ffe973C72356C25e623E2470172A69::MyLibrary::init_library -s 0x07Ffe973C72356C25e623E2470172A69 -b
# get library
state get resource 0x07Ffe973C72356C25e623E2470172A69 0x07Ffe973C72356C25e623E2470172A69::MyLibrary::Library
# add book
account execute-function --function 0x07Ffe973C72356C25e623E2470172A69::MyLibrary::s_add_book --arg b"web3" --arg b"github.com" -s 0x07Ffe973C72356C25e623E2470172A69 -b
# update book at index
account execute-function --function 0x07Ffe973C72356C25e623E2470172A69::MyLibrary::s_update_book_at_id --arg 0 --arg b"atest" --arg b"noncegeek.com" -s 0x07Ffe973C72356C25e623E2470172A69 -b
# delete book at index
account execute-function --function 0x07Ffe973C72356C25e623E2470172A69::MyLibrary::s_add_book --arg b"web3" --arg b"github.com" -s 0x07Ffe973C72356C25e623E2470172A69 -b

通过如下命令在starcoin console中进行合约部署:

starcoin% dev deploy [path to blob] -s [deployer addr] -b

初始化 Library:

starcoin% account execute-function --function [deployer addr]::MyLibrary::init_library -s [deployer addr] -b

查看 Library:

starcoin% state get resource [caller addr] [deployer addr]::MyLibrary::Library

此时因为还没有藏书,所以图书馆是空的:

image-20220801210648788

插入一本 Book:

starcoin% account execute-function --function [deployer addr]::MyLibrary::s_add_book --arg b"web3" --arg b"github.com" -s [caller addr] -b

此时再执行get resource,会发现多了一个 item:

image-20220801211027796

更新(Update)和删除(Delete)的操作同理,你自己来动手试试吧:)!

0x05 Library 合约源码分析

Move 合约包含Structfuncscript三个部分。

其中,Struct定义数据结构、func是一般函数、script是暴露被外部调用的脚本。

module MyAddr::MyLibrary {
   use StarcoinFramework::Signer;
   use StarcoinFramework::Vector;
   // each ability has matching keyword
   // multiple abilities are listed with comma
   struct Book has store, copy, drop {
      id: u64,
      name: vector<u8>,
      link: vector<u8>
   }

   // single ability is also possible
   struct Library has key {
      books: vector<Book>
   }

   public fun create_library(account: &signer){
      move_to<Library>(account, Library{books: Vector::empty<Book>()});
   }
   //because the script function cannot have return value,
   //query only can be done by: state get resource Addr Addr::MyLibraryV4::Library
   public fun addBook(account: &signer,name:vector<u8>, link: vector<u8>) acquires  Library {
      let lib = borrow_global_mut<Library>(Signer::address_of(account));
      let id = Vector::length(&lib.books);
      Vector::push_back(&mut lib.books, Book{id:id,name:name,link:link});
   }

   public fun updateBookAtId(account: &signer,id:u64,name:vector<u8>, link: vector<u8>) acquires  Library {
      let lib = borrow_global_mut<Library>(Signer::address_of(account));
      let book = Vector::borrow_mut<Book>(&mut lib.books,id);
      book.name = name;
      book.link = link;
   }

   public fun deleteBookAtId(account: &signer,id:u64) acquires  Library {
      let lib = borrow_global_mut<Library>(Signer::address_of(account));
      Vector::remove(&mut lib.books, id);
   }

   public(script) fun init_library(account: signer){
      Self::create_library(&account)
   }

   public(script) fun s_add_book(account: signer, name:vector<u8>, link: vector<u8>) acquires  Library {
      Self::addBook(&account,name, link)
   }

   public(script) fun s_update_book_at_id(account: signer, id:u64,name:vector<u8>, link: vector<u8>) acquires  Library {
      Self::updateBookAtId(&account,id,name,link)
   }

   public(script) fun s_delete_book_at_id(account: signer, id:u64) acquires  Library {
      Self::deleteBookAtId(&account,id)
   }
}

5.1 move_to 函数

还记得 signer 吗?现在你可以看看它是如何运作的!要将资源移动到帐户,您有内置函数 move_to,它将signer作为第一个参数,Collection作为第二个参数。move_to 函数的签名可以表示为:

native fun move_to<T: key>(account: &signer, value: T);

这导致两个结论:

  • 您只能将资源放在您的帐户下。您无法访问另一个帐户的 signer value,因此无法将资源放在那里。
  • 一个地址下只能存储一种单一类型的资源。两次执行相同的操作会导致丢弃现有资源——这种情况绝不能发生(想象您存储了您的硬币,并且由于不准确的操作,您通过推空余额丢弃了所有储蓄!)。第二次尝试创建现有资源将失败并出现错误。

—— https://move-book.com/resources/resource-by-example/storing-new-resource.html

在本 demo 中,我们通过 move_to 函数创建了Library资源:

public fun create_library(account: &signer){
 move_to<Library>(account, Library{books: Vector::empty<Book>()});
}

5.2 borrow_global & borrow_global_mut

见:

https://move-book.com/cn/resources/resource-by-example/access-resource-with-borrow.html#%E8%AF%BB%E5%8F%96%E5%92%8C%E4%BF%AE%E6%94%B9-resource

5.3 Vector

Vector 是 Rust 中的一种数据类型,其允许我们在一个单独的数据结构中储存多于一个的值,它在内存中彼此相邻地排列所有的值。Vector 只能储存相同类型的值。它们在拥有一系列项的场景下非常实用,例如文件中的文本行或是购物车中商品的价格。

在本 Demo 中,我们使用vector<u8>来统一处理字符串,相当于 Python 中的 b"something",或 Elixir 中的 <<1,2,3>>。

此外,Library 中存放了很多书籍,我们能用vector<Book>表示,相当于其他语言中的List类型:

struct Library has key {
 books: vector<Book>
}

要了解更多关于 Vector 的内容,可以见starcoin-framework

https://github.com/starcoinorg/starcoin-framework/blob/3e24ac46c2ada60956bce1d104e7d7de3a42d849/sources/Vector.move


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存