区块链去中心化思想无处不在,比如最近使用个体抗原自检替代大规模的中心化核酸检测,就是去中心化思想的落地实践,避免了大规模聚集导致的交叉感染,提高了检测效率,本次我们使用Ethereum最新的ethersV5.0以上版本链接去中心化区块链钱包,并且通过后端Golang1.18服务进行验签。

在之前的一篇文章:青山不遮,毕竟东流,集成Web3.0身份钱包MetaMask以太坊一键登录(Tornado6+Vue.js3)中,我们使用的是ethersV4.0版本链接Metamask钱包,后端使用基于Python3.10的Tornado6.0框架,为了避免同质化,这里换成Okc钱包,客户端插件安装地址:https://chrome.google.com/webstore/detail/okx-wallet/mcohilncbfahbmgdjkbpemcciiolgcge

前端链接浏览器钱包

首先卸载Vue2.0项目:

npm uninstall vue-cli -g

这里node版本要在8.9以上,npm版本要在6以上;

随后安装Vue3.0以上版本:

npm install -g @vue/cli

然后安装pnpm:

npm install -g pnpm

pnpm解决了传统npm的node_modules依赖困境,主要通过软链接和硬链接的结合使用,最终达到节省磁盘空间,安装速度快,严格高效等目的,这里推荐使用pnpm进行包管理。

接着,在当前项目中安装ethers库:

pnpm install ethers@5.7.2 --save

注意这里版本要求v5.0以上。

根据ethers5.4官方文档所述:https://docs.ethers.io/v5/getting-started/#getting-started–connecting-rpc

ethers5.0版本支持异步async操作,提高了效率,async函数就是使用async关键字声明的函数。它是 AsyncFunction 构造函数的实例,并且其中允许使用 await 关键字。async 和 await 关键字让我们可以用一种更简洁的方式写出基于 Promise 的异步行为,而无需刻意地链式调用 promise。

声明异步链接方法:

//链接逻辑      connect:async function(){      },

随后请求链接当前的区块链钱包,并且异步获取公钥地址:

const provider = new ethers.providers.Web3Provider(window.ethereum);    const accounts = await provider.send("eth_requestAccounts", []);

打印钱包地址:

console.log(accounts);

如图所示:

这里已经打印出了okc钱包的公钥地址,随后生成签名:

const signer = provider.getSigner();      var rightnow = (Date.now()/1000).toFixed(0)                console.log(rightnow);                signer.signMessage("Signing in at "+rightnow)                .then((signature) => {                              //打印签名和公钥                          console.log(accounts[0],signature);                });

这里通过provider对象获取签名者对象signer,接着调用signMessage方法来进行签名操作,加签算法采用最简单的字符串+时间戳的形式。

前端返回签名和公钥地址:

0x5cae6c39a56d99d68e7a20c76da0ec387e34249b 0x1093b6dc7c6ae1340b2ebcf819dac1a7160b69a2abbb14d86a0696bd96d6b36923d5f3f82588f30a9353b327014338f51d4e7a90baa8052791a8017f156b57511c

后端Golang验签

验签的目的很好理解,如果在链接钱包的一瞬间,客户端被监听的其他软件恶意篡改公钥地址,那么很可能会给客户造成不可挽回的经济损失,所以暴露在前端的一切数据都需要后端进行校验,之前我们采用的是Python3.10版本进行验签操作:

from web3.auto import w3  from eth_account.messages import defunct_hash_message  import time    public_address = "0x5cae6c39a56d99d68e7a20c76da0ec387e34249b"  signature = "0xc7b06789e6710652d8540487055e0e75918c9c4366ec47c9e7008760df1dedd6506a908f466e448481afed3fe009bbdbfdfa16c28585eff68be54d600083d4251b"      #rightnow = int(time.time())    rightnow = 1670142219    print(rightnow)         original_message = 'Signing in at {}'.format(rightnow)    message_hash = defunct_hash_message(text=original_message)    signer = w3.eth.account.recoverHash(message_hash, signature=signature)    print(signer)

程序返回:

1670142219  0x5cAE6c39A56d99d68e7A20c76da0ec387e34249b

这里通过签名反向解析出了公钥地址,并且和前端获取的地址保持一致。

下面我们采用Golang1.18版本来验签,看看有什么不一样,首先安装Golang1.18,请移步:兔起鹘落全端涵盖,Go lang1.18入门精炼教程,由白丁入鸿儒,全平台(Sublime 4)Go lang开发环境搭建EP00

随后安装基于Golang的Ethereum库:

go get github.com/storyicon/sigverify

根据官方文档指引:https://github.com/storyicon/sigverify

构建main.go文件:

package main    import (  "fmt"    ethcommon "github.com/ethereum/go-ethereum/common"  "github.com/storyicon/sigverify"  )    func main() {  valid, err := sigverify.VerifyEllipticCurveHexSignatureEx(  ethcommon.HexToAddress("0x5cae6c39a56d99d68e7a20c76da0ec387e34249b"),  []byte("Signing in at 1670142219"),  "0xc7b06789e6710652d8540487055e0e75918c9c4366ec47c9e7008760df1dedd6506a908f466e448481afed3fe009bbdbfdfa16c28585eff68be54d600083d4251b",  )  fmt.Println(valid, err) // true   }

这里sigverify.VerifyEllipticCurveHexSignatureEx方法有三个参数,分别是公钥地址,签名字符集以及前端返回的签名字符串,返回值为valid:

➜  mydemo git:(master) ✗ go run "/Users/liuyue/wodfan/work/mydemo/src/mytest.go"  true 

如果验签通过会返回布尔值:true。

至此,后端验签流程就结束了。

结语

总体而言,前端Ethers采用了ES7新语法async/await实现了重大改进,它提供了一种使用同步代码样式异步链接钱包对象的方式,而且不会阻塞主线程,而后端Golang作为编译型语言验签流程反而比解释型的Python更加简单方便。