查看原文
其他

DID中地址聚合器的实现 | Move dApp 极速入门(八)

天蓝&李大狗 李大狗Leeduckgo 2022-10-07

在本文中我们将介绍 DID 合约 @Starcoin 的 AddrAggregator (地址聚合器)部分的实现。

仓库地址:

https://github.com/NonceGeek/DID-Solution-In-Move

本文包括如下知识点:

  • 资源的概念与使用
  • 合约操
  • 签名算法
  • 数据的增删改查
  • Starcoin 控制台的使用

0x01 准备工作

首先,按照如何设置本地开发网络中的描述启动一个开发网络,并获得一些 STC。

在本文档中,我将使用我的开发网络的默认帐户地址 0xa8db68190dc89c14e387e1bfb454d088来创建一个did合约。

创建自定义 did 的代码源文件位于 did。

1.1 环境准备

1、下载最新版本的starcoin安装包,把解压后的目录配置到环境变量里
下载地址:
https://github.com/starcoinorg/starcoin/releases
2、打开一个控制台,启动节点
starcoin -n dev
3、打开一个新的控制台,启动交互式环境
starcoin  -n dev  console

1.2 账号准备

# 新建两个账号一个用来部署合约以及初始化合约,另一个用来调用did合约相关方法 
starcoin% account create -p 123456
# 这里生成账号(用于部署合约):0xa8db68190dc89c14e387e1bfb454d088
# 通过控制台给账号充钱
starcoin% dev get-coin [acct you get from create cmd] -v 2000STC

1.3 DID科普

DID 是由w3c 推出的数字身份协议, Decentralized identifiers (DIDs) 是是一种新型标识符,可实现可验证的去中心化数字身份。具体可查阅did文档

DID标识符:

did = "did:" 方法名称 ":" 方法特定 ID; 例子:did:example:id

1.4 AddrAggregator 功能分析

写一个给不同地址分配的ID的合约,这里简化了DID定义,对于地址对应的是一个ID,实际上就是一个地址聚合器 主要包含功能有:

  • 初始化地址聚合器

  • 添加地址,生成地址对象,产生唯一 ID,获取当前区块高度作为待签名的信息,记录创建时间

  • 更新地址对象的签名以及时间

  • 删除地址

0x02 合约实现

代码总共包含4个合约, AddrAggregator是DID处理聚合器、EthSigVerifierV5是eth签名验证,Util是公共方法的封装

这里主要分析AddrAggregator合约,这也是did的核心

定义AddrInfo,包含地址详细信息
struct AddrInfo has store, copy, drop {
  addr: address,  //地址
  description: vector<u8>,  //描述
  chain_name: vector<u8>,  //链名
  msg: vector<u8>, //msg信息
  signature: vector<u8>, //签名
  created_at: u64, //创建时间
  updated_at: u64, //更新时间
  id : u64, //id
}

定义AddrAggregator,包含AddrInfo的集合、最大ID
struct AddrAggregator has key {
  key_addr: address,
  addr_infos: vector<AddrInfo>,  //地址信息集合
  max_id : u64     //当前最大id
}

AddrAggregator中的函数:初始化AddrAggregator

public (script) fun create_addr_aggregator(sender: signer){
  //定义AddrAggregator结构
  let addr_aggr =  AddrAggregator{
      key_addr: Signer::address_of(&sender),
      addr_infos: Vector::empty<AddrInfo>(),
      max_id : 0
  };
  //把AddrAggregator资源移动到sender的账户下 
  move_to<AddrAggregator>(&sender, addr_aggr);
}

添加地址

public (script) fun add_addr(sender: signer, 
  addr: address, 
  chain_name: vector<u8>,
  description: vector<u8>) acquires AddrAggregator {
  //获取AddrAggregator资源
  let addr_aggr = borrow_global_mut<AddrAggregator>(Signer::address_of(&sender));   
  //max_id加1
  let id = addr_aggr.max_id + 1;
  
  //获取区块高度
  let height = Block::get_current_block_number();
  let msg = Utils::u64_to_vec_u8(height);
  let now = Timestamp::now_seconds();
      
  //记录创建时间、height作为addr_info的msg
  let addr_info = AddrInfo{
      addr: addr, 
      chain_name: chain_name,
      description: description,
      signature: x"",
      msg: msg,   
      created_at: now,
      updated_at: 0,
      id : id,
  };

  //把addr_info加入addr_infos中 
  Vector::push_back(&mut addr_aggr.addr_infos, addr_info);
  //addr_aggr的max_id加1
  addr_aggr.max_id = addr_aggr.max_id + 1;
}

获取地址的待签名信息

public fun get_msg(contract: address, addr: address) :vector<u8> acquires AddrAggregator {
  //获取AddrAggregator资源
  let addr_aggr = borrow_global_mut<AddrAggregator>(contract);
  let length = Vector::length(&mut addr_aggr.addr_infos);
  let i = 0;

  while (i < length) {
      let addr_info = Vector::borrow<AddrInfo>(&mut addr_aggr.addr_infos, i);
      //地址匹配
      if (addr_info.addr == addr) {
        //返回待签名信息
        return *&addr_info.msg
      };
  };

  return x""
}

更新地址签名

public (script) fun update_addr_with_sig(sender: signer, 
  addr: address, signature : vector<u8>) acquires AddrAggregator {
  //获取AddrAggregator资源
  let addr_aggr = borrow_global_mut<AddrAggregator>(Signer::address_of(&sender));

  let length = Vector::length(&mut addr_aggr.addr_infos);
  let i = 0;
  while (i < length) {
      let addr_info = Vector::borrow_mut<AddrInfo>(&mut addr_aggr.addr_infos, i);
      //地址匹配
      if (addr_info.addr == addr) {
        //空msg判断
        if (*&addr_info.msg == x"") {
            abort 1001
        };

        // verify the signature for the msg 
        // kecacak256哈希运算
        let msg_hash = Hash::keccak_256(*&addr_info.msg); //kecacak256 hash 
        if (!EthSigVerifierV5::verify_eth_sig(copy signature, addr, msg_hash)) {
            abort 1002
        };
        
        // verify the now - created_at <= 2h 
        //时限判断
        let now = Timestamp::now_seconds();
        if (now - addr_info.created_at > 2*60*60) {
            abort 1003
        };

        // update signature, updated_at 
        //更新签名,时间
        addr_info.signature = signature;
        addr_info.updated_at = now;
        break
      };
      i = i + 1;
  };
}

删除地址

public (script) fun delete_addr(
  sender: signer,  
  addr: address) acquires AddrAggregator{
  //获取AddrAggregator资源
  let addr_aggr = borrow_global_mut<AddrAggregator>(Signer::address_of(&sender));
  let length = Vector::length(&mut addr_aggr.addr_infos);
  let i = 0;
  while (i < length) {
      let addr_info = Vector::borrow(&mut addr_aggr.addr_infos, i);
      //地址匹配
      if (addr_info.addr == addr) {
        //从addr_infos删除地址
        Vector::remove(&mut addr_aggr.addr_infos, i);
        break
      };
      i = i + 1;
  }
}

0x03 合约部署

修改 Move.toml 文件中 SNFT 地址为默认账户地址:

[addresses]
StarcoinFramework = "0x1"
MyAddr = "0xa8db68190dc89c14e387e1bfb454d088"

在 shell 控制台中运行 mpm release 以获取发布模块:

$ mpm release
Packaging Modules:
         0xa8db68190dc89c14e387e1bfb454d088::Utils
         0xa8db68190dc89c14e387e1bfb454d088::EndpointAggregatorV5
         0xa8db68190dc89c14e387e1bfb454d088::EthSigVerifierV5
         0xa8db68190dc89c14e387e1bfb454d088::AddrAggregator
Release done: release/did.v0.0.1.blob, package hash: 0x624a7e37094eeae02411d7991ca61b973a76b8a5908823c424ea73c694db46ff

它将编译合约代码,在项目的 release 目录下生成二进制文件 did.v0.0.1.blob,此二进制文件将用于之后的部署。

解锁帐户并部署 did 合约:

starcoin% account unlock 0xa8db68190dc89c14e387e1bfb454d088 -p <MY-PASSWORD>
starcoin% dev deploy /path/to/did/release/did.v0.0.1.blob -s 0xa8db68190dc89c14e387e1bfb454d088 -b

txn 0xda09b71180b3b2ba924d8e983c13a3415b2b2ff6f5eeb0f8d53ed4458c872eba submitted.
{
  "ok": {
    "dry_run_output": {
      "events": [],
      "explained_status""Executed",
      "gas_used""28760",
      "status""Executed",
  .....
  ....
}

可以看到交易已经提交,结果状态为 Executed。这意味着该模块已部署。

0x04 执行脚本功能

4.1 初始化

首先调用create_addr_aggregator进行初始化。

account execute-function --function 0xa8db68190dc89c14e387e1bfb454d088::AddrAggregator::create_addr_aggregator -s 0xa8db68190dc89c14e387e1bfb454d088 -b

调用资源查看命令,可以看到addr_infos为空,max_id为0。

state get resource 0xa8db68190dc89c14e387e1bfb454d088 0xa8db68190dc89c14e387e1bfb454d088::AddrAggregator::AddrAggregator

返回:

{
  "ok": {
    "json": {
      "addr_infos": [
      ],
      "key_addr""0xa8db68190dc89c14e387e1bfb454d088",
      "max_id": 0
    },
    "raw""0xa8db68190dc89c14e387e1bfb454d088"
  }
}

4.2 增加地址

调用add_addr增加地址:

account execute-function --function 0xa8db68190dc89c14e387e1bfb454d088::AddrAggregator::add_addr -s 0xa8db68190dc89c14e387e1bfb454d088 --arg x"14791697260E4c9A71f18484C9f997B308e59325"  --arg b"ethereum" --arg b"test_addr"

返回Executed,调用成功。

调用资源查看命令,可以看到addr_infos已经有值,max_id为1, 从返回结果可以看出,addr_infos已经有了0x14791697260e4c9a71f18484c9f997b308e59325的地址相关信息,包括chain_name, description, msg, id, created_at等信息。

state get resource 0xa8db68190dc89c14e387e1bfb454d088 0xa8db68190dc89c14e387e1bfb454d088::AddrAggregator::AddrAggregator

返回:

{
  "ok": {
    "json": {
      "addr_infos": [
        {
          "addr""0x14791697260e4c9a71f18484c9f997b308e59325",
          "chain_name""0x657468",
          "created_at": 36,
          "description""0x65746861646472",
          "id": 1,
          "msg""0x372e6e6f6e63655f6765656b",
          "signature""0x",
          "updated_at": 0
        }
      ],
      "key_addr""0xa8db68190dc89c14e387e1bfb454d088",
      "max_id": 1
    },
    "raw""0xa8db68190dc89c14e387e1bfb454d088011414791697260e4c9a71f18484c9f997b308e593250765746861646472036574680104002400000000000000000000000000000001000000000000000100000000000000"
  }
}

获取特定地址的msg信息,用作后面签名适用的待签名信息。

dev call --function 0xa8db68190dc89c14e387e1bfb454d088::AddrAggregator::get_msg --arg 0xa8db68190dc89c14e387e1bfb454d088  --arg x"14791697260E4c9A71f18484C9f997B308e59325"

返回:

{
  "ok": [
    "0x372e6e6f6e63655f6765656b"
  ]
}

记得要将 hex binary 做一下 Base16.decode,得到待签名字符串:

4.3 用 eth.build 签名

通过 eth.build,我们可以容易地使用 ethereum 的签名算法对信息进行签名。

https://eth.build/build#62a34ce54c284dc5dd430b1edf767ecca405a8b58e55797be3ed114c964138d6

image-20220927204454119

可以使用私钥直接签,也可以调起 Metamask 进行签名。

4.4 给地址添加签名

更新地址的签名(拿到上一步的待签名信息,计算签名后作为参数传入)。

account execute-function --function 0xa8db68190dc89c14e387e1bfb454d088::AddrAggregator::update_addr_with_sig -s 0xa8db68190dc89c14e387e1bfb454d088 --arg x"14791697260E4c9A71f18484C9f997B308e59325" --arg x"e02fcfd6e2f9d7a1aa6b12cef95bb6e5013b14dc584e080034b09f902fd87d8b4e46d83462ae957f8db86f50aac61dde24261befd1c11780adc8e0d5ee4e34971c" 

返回 Executed,调用成功。

调用资源查看命令,可以看到14791697260E4c9A71f18484C9f997B308e59325的signature和updated_at字段已经更新:

state get resource 0xa8db68190dc89c14e387e1bfb454d088 0xa8db68190dc89c14e387e1bfb454d088::AddrAggregator::AddrAggregator

返回:

{
  "ok": {
    "json": {
      "addr_infos": [
        {
          "addr""0x14791697260e4c9a71f18484c9f997b308e59325",
          "chain_name""0x657468",
          "created_at": 36,
          "description""0x65746861646472",
          "id": 1,
          "msg""0x04",
          "signature""0xe02fcfd6e2f9d7a1aa6b12cef95bb6e5013b14dc584e080034b09f902fd87d8b4e46d83462ae957f8db86f50aac61dde24261befd1c11780adc8e0d5ee4e34971c",
          "updated_at": 43
        }
      ],
      "key_addr""0xa8db68190dc89c14e387e1bfb454d088",
      "max_id": 1
    },
    "raw""0xa8db68190dc89c14e387e1bfb454d088011414791697260e4c9a71f18484c9f997b308e59325076574686164647203657468010441e02fcfd6e2f9d7a1aa6b12cef95bb6e5013b14dc584e080034b09f902fd87d8b4e46d83462ae957f8db86f50aac61dde24261befd1c11780adc8e0d5ee4e34971c24000000000000002b0000000000000001000000000000000100000000000000"
  }
}

调用delete_addr删除地址。

account execute-function --function 0xa8db68190dc89c14e387e1bfb454d088::AddrAggregator::delete_addr  -s 0xa8db68190dc89c14e387e1bfb454d088 --arg x"14791697260E4c9A71f18484C9f997B308e59325"

返回 Executed,调用成功。

此时再次调用资源查看命令,可以看到addr_infos为空,max_id为1,符合预期。

state get resource 0xa8db68190dc89c14e387e1bfb454d088 0xa8db68190dc89c14e387e1bfb454d088::AddrAggregator::AddrAggregator

返回:

{
  "ok": {
    "json": {
      "addr_infos": [],
      "key_addr""0xa8db68190dc89c14e387e1bfb454d088",
      "max_id": 1
    },
    "raw""0xa8db68190dc89c14e387e1bfb454d088000100000000000000"
  }
}

4.5 练习

0x01)目前添加地址的方式还存在安全风险,你知道是哪方面的安全风险吗?

0x02)目前只能添加符合 ethereum 格式规范的地址,请补充一个函数,让其能添加 ed25519(starcoin)的地址。

答案回复链接:

https://github.com/NonceGeek/Web3-dApp-Camp/discussions/136


前文链接:
Aptos 中的智能合约形式化验证 | Move dApp 极速入门(七)
Aptos CLI使用指南与REPL设计建议 | Move dApp 极速入门(六)
实现一套 DID 之总体设计 | Move dApp 极速入门(五)
合约数据类型综述 | Move dApp 极速入门(四)
操作资源 | Move dApp极速入门(三)
第一个 Move dApp | Move dApp极速入门(二)
Hello Move | Move dApp极速入门(一)


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

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