新版以太坊Ethereum库ethersV5.0配合后端Golang1.18实时链接区块链钱包(Metamask/Okc)以及验签操作

by Liu Yue/2022-12-04

    区块链去中心化思想无处不在,比如最近使用个体抗原自检替代大规模的中心化核酸检测,就是去中心化思想的落地实践,避免了大规模聚集导致的交叉感染,提高了检测效率,本次我们使用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 <nil>
}

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

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

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

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

    结语

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