sui move 的对象存储状态和能力(ability)

  • jinglin
  • 更新于 2024-11-14 16:01
  • 阅读 180

Move对象的有4种能力(abliity)1.对象的几种存储状态:新建的对象,进入未曾存储状态,一个交易结束之后,对象要么被销毁,要么被存储到网络的链上存储。链上存储的对象,也可以借用引用参与交易运算运行态的对象,可以通过share或freeze编程共享对象。freeze

Move 对象的有4种能力(abliity)

1. 对象的几种存储状态:

  • 新建的对象, 进入未曾存储状态,一个交易结束之后,对象要么被销毁,要么被存储到网络的链上存储。

  • 链上存储的对象,也可以借用引用参与交易运算

  • 运行态的对象,可以通过share或freeze 编程共享对象。freeze的对象是只读的。

  • 链上存储对象,可以通过函数调用作为输入参数,移动成运行态,进入运行态之后,又可以重新进入析构或重新存到链上。

image.png

2. 对象具有的四种ability (能力)

这四种能力是:

  • copy

    • 允许复制具有此功能的类型的值。
    • 当赋值或传参数时,会自动拷贝成新对象
    • 如果没有实现copy能力, 赋值的时候,是对象被移动
  • drop

    • 允许跳出作用域自动删除
  • store

    • 允许具有此功能的类型的值存在于 链上中的值中。 存在sui 网络上的对象的字段
    • 对于 Sui,控制对象内部可以存储哪些数据。 还控制哪些类型可以在其定义模块之外传
  • key

    • 允许类型用作存储的 “key”。表面上,这意味着该值可以是 存储中的顶级价值;换句话说,它不需要包含在另一个值中,以 在仓库里。

    • 对于 Sui,用于表示对象

2.1 如果有copy能力,赋值就是拷贝成一个新对象

image.png

  • 上图中27行,c对象被拷贝到d,c和d指向的不是同一个对象,

  • 28 行修改了d,但是c没有被修改

  • 32行和33行中,c和d取值不同.

2.2 如果没有copy 能力,赋值就是移动对象

image.png

第111 行中出现编译错误,因为 c 指向的对象已经转移到了d

    public struct NonCopyable {
        value :u64,
    }

    #[test]
    fun test_non_copy(){
        let c = NonCopyable{ value:33};
        let d =c ;
        std::debug::print(&c); //此时c对象已经被移动给d
        std::debug::print(&d);

       // sui::test_utils::destroy(c);
        sui::test_utils::destroy(d);
    }   

2.3 销毁 (drop)

2.3.1 离开作用域自动销毁,需要具有drop能力

   public struct IgnoreMe has drop{
      a:u32,
      b:u32
  }

  #[test]
  fun test_ignore(){
      let im = IgnoreMe{a:3,b:4};
      print(&im);
  }

上文定义的IgnoreMe 对象im 具有自动析构的能力has drop,在离开函数的作用域时候自动析构.

2.3.2 没有drop能力的对象需要主动析构或保存到链上

   public struct NoDrop{ value :u64 }

    #[test]
    fun test_nodrop(){
        let no_drop = NoDrop{ value :34};
        print(&no_drop);

        let NoDrop{ value: _ } = no_drop;
    }

这个例子 NoDrop类型没有drop能力,对象离开作用域,需要析构,或者将对象的所有权转移.

  • 第8行是析构对象的代码

2.3.4 不能析构时,能转移所有权:

    fun useNoDrop(o : NoDrop ) : NoDrop{
        std::debug::print(&o);
        o
    }

    #[test]
    fun testUseNoDrop(){
        let o = NoDrop{value :4};
        let d = useNoDrop(o);
        NoDrop{value:_} = d;
    }
  • 第1行函数UseNoDrop获得了对象o
  • 第3行,函数将N哦Drop对象o 返回出去,将所有权转移出去.
  • 第10 行,显示代码析构这个NoDrop对象

2.3.5 独立存储在链上

对象独立存储在链上,必须具有key能力 has key

image.png

    public struct Keyable has key{
        id : UID,
    }

    #[test]
    fun test_key(){
        let mut ctx = tx_context ::dummy();
        let k = Keyable{ id: object::new(&mut ctx)};
        std::debug::print(&k);
        transfer::transfer(k,ctx.sender());
    }

这个例子中Keyable是具有key能力的,可以通过transfer传递到sui网络上存储, 指定owner地址.

2.3.6 对象作为 共享对象存储在链上,需要具有key能力

image-20241114091033160

2.3.7 对象作为一个对象的一部分存储,需要具有store能力

    use std::string::String;
    public struct Grandson has store{
        name : String,
    }
    public struct Child has store{
        name : String,
        child : Grandson,
    }
    public struct Parent has key{
        id: UID,
        child: Child,
    }
    #[test]
    fun  test_store_child(){
        let mut ctx = tx_context::dummy();
        let foo = Parent {
            id : object::new(&mut ctx),
            child: Child {
                name : b"one child".to_string(),
                child: Grandson{
                    name : b"a grandson".to_string(),

                },
            }
        };
        std::debug::print(&foo);
        transfer::freeze_object(foo);
    }
  • Parent是独立存储,具有key能力

  • Child 都是作为独立存储对象Parent的字段

  • Grandson 作为Child的字段存储在链上,

  • Child 和Grandson 都具有store能力

image.png

2.3.8 无ablity 对象就必须在module内提供方法去析构它

  • 一个对象,没有任何ability ,就是hot potato

  • 没有key 和store 无法保存在链上

  • 没有drop 不会自动析构

  • 只能在本模块析构, 因为外部模块无法直接访问struct的字段

  • 下图中模块A, 需要使用模块B中的Foo 对象,使用完毕需要调用模块B 的析构方法

    public struct Foo {
      value :u32
    }
    

image.png image-20241114152404111

2.3.8.1 根据对象不能外部析构的特性,构造借贷模型

image-20241114080921239

image.png

2.3.8.2 闪电贷的逻辑

image.png

  • 定义闪电贷的出借方,在borrow的时候获得Coin和 Loan对象,
  • 在repay的时候才能销毁Loan对象, 如果借出放不调用repay,无法销毁Loan对象,交易会回滚.
  • replay的时候需要校验传入的coin 要多于borrow的coin, 多出来的部分作为回报.
    • 下面是 贷方代码的例子
module book::loan;
//hot potato pattern
public struct Loan{
    feedback: u64, //还钱数
}

public struct Coin has key,store{
    id:UID,
    amount:u64
}

//借出钱
public fun borrow(amount:u64,ctx:&mut TxContext) :(Coin,Loan){
    let feedback = amount * 103 /100;
    let c = Coin{ id: object::new(ctx),amount};
    (c, Loan{feedback}) 
}

const ErrNotEnoughFeedback:u64 = 43;
const OWNER_ADDR :address = @0xafe36044ef56d22494bfe6231e78dd128f097693f2d974761ee4d649e61f5fa2;//todo

public fun repay(coin: Coin,loan:Loan){
    assert!(coin.amount >= loan.feedback,ErrNotEnoughFeedback);
    transfer::public_transfer(coin,OWNER_ADDR);
    Loan{ feedback:_} = loan;
}

``` {.line-numbers}

下面是借方模块的代码,使用测试代码演示

```c++
#[test_only]
module book::test_loan;
use book::loan::Coin;

////todo 贷款后赚钱的业务逻辑,这里没有实现
fun earn_money(coin : Coin ) :Coin{
    coin
}

#[test]
#[expected_failure(abort_code=book::loan::ErrNotEnoughFeedback)]
fun borrow_test(){
    let mut ctx = tx_context::dummy();
    let (coin,loan) = book::loan::borrow(233,&mut ctx);
    let new_money = earn_money(coin);
    book::loan::repay(new_money,loan);
    //todo 赚钱了,可以把feedback-amount 的钱,存入自己钱包
}

2.3.10 key + store ability

目前其他模块存到链上的对象,都需要key+store能力

public fun public_transfer<T: key + store>(obj: T, recipient: address) {
    transfer_impl(obj, recipient)
}
public fun public_freeze_object<T: key + store>(obj: T) {
    freeze_object_impl(obj)
}

public fun public_share_object<T: key + store>(obj: T) {
    share_object_impl(obj)
}
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
jinglin
jinglin
0xf54D...aDcd
http://github.com/nextuser