手把手教你MetaID应用开发

做为一名姿(这不是错别字)深前端开发工程师,随着区块链热潮的兴起,本人在3年前就开始接触区块链,嗯。。。没错,就是从17年人人暴富的炒币开始。
区块链认知水平:去中心化,pow,pos,区块链,侧链,闪电链,公链,联盟链,私链(有没有这玩意儿?)

嗯。。。。这些关键字我都记住了。

以前没做过区块链的相关项目,大家都一样,从头捋

为什么是基于MetaID:主要是MetaID的推行方完善了生态,SDK,数据服务,钱包都已封装好了,要不然我一个前端上演全垒打,这得啥时间才能完成一个小项目,想想就GG
项目地址: ShowTalk
技术栈:vue2.0 + vue-cli3.0 + vuex + vue-router + sass + mongodb
已实现功能:
创建,加入,退出聊天室
聊天室加密
聊天信息加密
未读消息提示
消息上链费用提示
多端适配
用户加入房间信息提示
moji表情
项目截图:

手把手教你MetaID应用开发_第1张图片
手把手教你MetaID应用开发_第2张图片
手把手教你MetaID应用开发_第3张图片
已经有不少区块链技术爱好者加入队伍

新手上路三步走

1.新手任务,做一个基于MetaID的聊天室
2.获取在新手村生活需要的资源

  • Metaid:官网,协议规范(就是约定链上数据数据格式)

     
    

    MetaID是基于Metanet的二级协议规范,具体去官网查看详细解释

  • Metaid Protocol:官网,已经创建并公开的基于MetaID的协议都在这

    其实就是约定协议规范里部分的数据格式
    
  • ShowDB:官网,链上数据服务

    提供基于metaid协议链上数据的查询服务(公司做了一份链上数据的拷贝,加快查询速度)
    
  • ShowMoney:官网,bsv钱包服务

从以上资源不难看出,MetaID是推广某种事实上的规范,方便链上数据互通,为推动区块链技术的发展做出贡献(伟大的愿景)

3.查阅完成新手任务需要的知识

  • 参考文档:链接,前9节看完心里有个大概的概念就可以了
  • Metanet协议(了解) 参考文档
  • 比特币交易(utxo)(了解) 参考文档

以上所说的文档之类,看过,心里有数即可,遇到问题随时翻阅

基础知识了解了,现在来看做一个聊天室需要哪些功能:
1.用户登录/注册
2.创建聊天室 协议
3.加入聊天室 协议
4.退出聊天室 协议
5.聊天 协议

这五个操作都需要上链,上链就需要钱包创建交易,自建钱包交易对于我们初学者来说肯定不可行,难度太高,使用支持MetaID的钱包服务是首选,使用MetaID标准发行方的Showmoney创建账号,在创建账号的时候showmoney默认生成了metaID节点,节点包含用户信息。以后的登录就采用showmoney授权的方式进行。
既然有钱包服务,那创建交易之类的逻辑肯定得有配套得sdk啦,ShowMoney提供了metaidjs 来提供创建创建协议内容节点。协议节点之间得关联,sdk内部已经帮你处理好了(屏蔽细节加速开发)

2,3,4,5的操作需要定义协议节点内容,这部分工作的意义就在于约定链上数据的格式,方便应用间数据互通,其它个人或者机构可以方便的获取对应数据,消除信息孤岛。

以发一条聊天信息为例:

	// 协议格式
      const protocolData = {
     
        groupID, "房间的nodeid"
        timestamp,//创建消息的时间戳
        nickName,//群聊的昵称,我默认使用创建MetaID对用的名字
        content: cryptMessage(content, mataid.substring(0, 16)),//消息内容,
        //我这是为了避免明文传输,把消息进行了加密,解密方式要包含在某种信息里,自行约定
        contentType: "text/plain", //内容格式
        encryption: "aes" //加密方式
      };
      // 插入节点
      const node = {
     
        nodeName: "simpleGroupChat",
        metaIdTag: process.env.VUE_APP_IDtags,
        brfcId: "80395c27b6eb",
        encrypt: 0,
        payCurrency: "usd",
        path: "/Protocols/simpleGroupChat",
        dataType: "application/json",
        data: JSON.stringify(protocolData),
        accessToken
      };
      metaIdJs.addProtocolNode(node)

数据上链已经完成,操作得成功与否取决于你得ShowMoney账户上是否有余额可以支付这次交易,根据数据内容大小每次上链所需费用不同,上链有最低消费-_-!,即使你发了条空白消息也有那么多数据不是

创建房间需要一条交易,加入房间需要一条交易,退出房间需要一条交易,聊天也需要一条交易,嗯。。。。。。。万般操作皆交易!!!

现在开始组织数据,感受一下
以获取已加入房间用户为例,上代码:

    async getGroupUser(groupID) {
     
      // 根据最新退出房间时间判断是否是有效用户
      let cache = {
     };
      let userlist = [];
      const joinRes = await getProtocolData([["SimpleGroupJoin", {
      groupID }]], {
     }, 0, 1000);
      const leaveRes = await getProtocolData([["SimpleGroupLeave", {
      groupID }]], {
     }, 0, 1000);
      const creater = await getProtocolData("SimpleGroupCreate", {
      metanetId: groupID });
      for (let element of joinRes.data) {
     
        if (cache[element.rootTxId]) {
     
          continue;
        }
        cache[element.rootTxId] = element.timestamp;
      }
      if (!cache[creater.data[0].rootTxId]) {
     
        cache[creater.data[0].rootTxId] = creater.data[0].timestamp;
      }
      // 记录最后一次用户加入房间的时间点
      if (!this.groupGather[groupID]) {
     
        this.groupGather[groupID] = {
     };
      }
      if (joinRes.data[0] && joinRes.data[0].timestamp) {
     
        this.groupGather[groupID].lastJoinTime = joinRes.data[0].timestamp;
      } else {
     
        this.groupGather[groupID].lastJoinTime = creater.data[0].timestamp;
      }
      // 记录最后一次用户离开的时间点
      if (leaveRes.data[0] && leaveRes.data[0].timestamp) {
     
        this.groupGather[groupID].lastLeaveTime = leaveRes.data[0].timestamp;
      }
      for (let element of leaveRes.data) {
     
        if (element.timestamp > cache[element.rootTxId]) {
     
          delete cache[element.rootTxId];
        }
      }
      let users = await batchGetUserInfo(Object.keys(cache));
      for (let item of users.data) {
     
        if (!this.userGather[item.metaId]) {
     
          if (!item.avatarTxId) {
     
            item.headUrl = process.env.VUE_APP_DEFAULT_AVATOR;
          } else {
     
            item.headUrl = process.env.VUE_APP_IMG_API + "metafile/" + item.avatarTxId;
          }
          this.$store.commit("addNewUser", {
     
            id: item.metaId,
            userInfo: item
          });
        }
        if (this.groupGather[groupID].userlist.indexOf(item.metaId) < 0) {
     
          userlist.push(item.metaId);
        }
      }
      this.groupGather[groupID].userlist.push(...userlist);
    }
    /**
 *
 * @param {String | Array} protocols 协议列表
 *  demo:['ShowText',['metanode',{isPrivate:0}],['metanote',{isPrivate:0}]]  || 'ShowText'
 * @param {Number} skip 偏移量
 * @param {NUmber} limit 数据限制量,默认10,负数为无限制
 * @param {Number} timestamp 排序,-1为倒序,1为顺序,默认倒序-1
 */
export function getProtocolData(protocols, config, skip = 0, limit = 10, timestamp = -1) {
     
  let protocol_list = [];
  let fin = {
     };

  if (!config) {
     
    config = {
     };
  }
  if (Object.prototype.toString.call(config) !== "[object Object]") {
     
    throw new Error("second param must be object {}");
  }

  if (typeof protocols !== "string" && !Array.isArray(protocols)) {
     
    throw new Error("first param must be Array or string");
  }

  if (typeof protocols === "string") {
     
    fin = {
     
      parentNodeName: protocols
    };
  } else {
     
    protocols.forEach(val => {
     
      if (typeof val !== "string" && !Array.isArray(val)) {
     
        throw new Error("item must be Array or string");
      }
      if (Array.isArray(val)) {
     
        let [parentNodeName, screen] = val;
        let obj = {
     };
        if (Object.prototype.toString.call(screen) !== "[object Object]") {
     
          throw new Error("if protocol item is array,the second item must be an object");
        } else {
     
          obj["parentNodeName"] = parentNodeName;
          for (let key in screen) {
     
            obj[`data.${
       key}`] = screen[key];
          }
          protocol_list.push(obj);
        }
      } else {
     
        protocol_list.push({
      parentNodeName: val });
      }
    });
    find = {
     
      $and: [
        {
     
          $or: [...protocol_list]
        }
      ]
    };
  }
  const query = {
     
    find: {
     
      ...find,
      metaId: process.env.VUE_APP_IDtags,
      ...config
      // isValid: true,
    },
    sort: {
     
      timestamp
    },
    skip,
    limit
  };
  return Api.ajaxGetUseBase64("/showMANDB/api/v1/query/queryFindMetaData/", query);
}

每次切换房间,需要先获取创建当前聊天室协议节点内容、加入当前聊天室协议节点列表,离开当前聊天室协议节点列表,组织这些数据的逻辑关系,判断是否是有效用户,开始你的表演。。。。。

后记

完成聊天室,修复bug,差不多整体用了一个月时间。
把聊天室代码整理一下就会开源,有问题在公共聊天室发言我看到后会回复

看资料花了一周

知道些概念就可以

vue学习用了两天

主流的前端框架,从应用的角度来说我们只需要关注基础语法,路由,状态管理,数据传递四个主要方面,其它高级特性知道就行,需要或者闲的时候再去研究,我们不需要写出狂拽酷炫吊炸天的代码,大部分代码的生命周期都不长,完成需求,项目死了你的代码价值就不大,项目好了自会有资源来完善更改,唯用主义(手动笑脸)

总结:
  • 区块链的项目需要一些想象力,当前依旧处于探索阶段,每种技术都有其边界,不是适用于所有应用场景。

未读消息提示仅能提示以应用登录为开始时间后续的未读聊天数,你想要存储的每种类型的数据都需要上链,如果加上未读开始时间记录,上链费用会大大增加 ,需要取舍

  • 上链的数据具备,去中心化(谁都可存,谁都可查),不可篡改,永久保存。

你可以看到当前房间的所有消息记录,不管你什么时间加入房间

  • MetaID的应用开发数据关联都由前端处理,给前端带来不小压力,好处也很明显,你不用再和服务器打交道,对于不熟悉服务器操作和语言的小伙伴来说是个福音。

还是要知道点数据库查询语句的,查询条件需要使用mongodb语法构建

  • 每个类型的交易详情都需要一次查询,所以前端请求存在大量并发的可能,给服务端带来不小的压力

关联型数据是常见业务场景,希望MetaID推行方丰富请求数据处理逻辑,sdk提供方便得关联数据查询方法

  • 目前基于utxo模型的交易有50限制,上链频率过高就容易触发此限制

此限制涉及到共识,安全方面,是bsv本身的限制,未来可能会去掉

你可能感兴趣的:(手把手教你MetaID应用开发)