分享web开发知识

注册/登录|最近发布|今日推荐

主页 IT知识网页技术软件开发前端开发代码编程运营维护技术分享教程案例
当前位置:首页 > 技术分享

MetaMask/zero-client

发布时间:2023-09-06 02:23责任编辑:沈小雨关键词:暂无标签

https://github.com/MetaMask/zero-client

MetaMask ZeroClient and backing iframe service

architecture

here is a comparison of the extension-based and iframe-based architecture

metamask extension:

dapp inpage.js <-> forwarder contentscript.js <-> extension background.jsui popup.js <-> extension background.js

就是当用户通过浏览器通过web3去调用json rpc时,即通过dapp inpage.js,再传给metamask contentscript,再传给metamask background,background将会将要进行的操作传给UI,使其能够在与rpc进行交互之前再一次得到用户的确认,用户确认后就会返回background去与rpc进行交互,然后再将得到的数据通过metamask contentscript-dapp inpage.js传给浏览器

current metamask iframe:

dapp inpage.js <-> iframe background.jsui app.metamask.io <-> iframe background.js

ideal metamask iframe:

dapp inpage.js <-> iframe forwarder <-> sharedworker background.jsui app.metamask.io <-> iframe forwarder <-> sharedworker background.js

ideal unified metamask extension+iframe:

dapp inpage.js <-> iframe forwarder <-> sharedworker background.jsui popup.js <-> iframe forwarder <-> sharedworker background.js

example

npm start

运行:

首先:

userdeMacBook-Pro:zero-client user$ npm install

然后运行npm start:

userdeMacBook-Pro:zero-client user$ npm start> metamask-zeroclient@2.0.0 start /Users/user/zero-client> ./example.sh./example.sh: line 3: beefy: command not found./example.sh: line 4: beefy: command not foundnpm ERR! file shnpm ERR! code ELIFECYCLEnpm ERR! errno ENOENTnpm ERR! syscall spawnnpm ERR! metamask-zeroclient@2.0.0 start: `./example.sh`npm ERR! spawn ENOENTnpm ERR! npm ERR! Failed at the metamask-zeroclient@2.0.0 start script.npm ERR! This is probably not a problem with npm. There is likely additional logging output above.npm ERR! A complete log of this run can be found in:npm ERR! ????/Users/user/.npm/_logs/2018-11-12T09_26_56_269Z-debug.log

出错,没有安装模块beefy:

什么是beefy及其使用

userdeMacBook-Pro:zero-client user$ npm install beefy --save

再运行又出错:

userdeMacBook-Pro:zero-client user$ npm start> metamask-zeroclient@2.0.0 start /Users/user/zero-client> ./example.shbeefy (v2.1.8) is listening on http://127.0.0.1:9001beefy (v2.1.8) is listening on http://127.0.0.1:9002200 ??58ms ??????261B ?/200 ??70ms ????1.95KB ?/bundle.js ? /Users/user/zero-client/node_modules/browserify /Users/user/zero-client/.index.js -dError: Cannot find module ‘metamask-crx/app/scripts/lib/inpage-provider.js‘ from ‘/Users/user/zero-client/lib‘ ???at /Users/user/zero-client/node_modules/browser-resolve/node_modules/resolve/lib/async.js:46:17 ???at process (/Users/user/zero-client/node_modules/browser-resolve/node_modules/resolve/lib/async.js:173:43) ???at ondir (/Users/user/zero-client/node_modules/browser-resolve/node_modules/resolve/lib/async.js:188:17) ???at load (/Users/user/zero-client/node_modules/browser-resolve/node_modules/resolve/lib/async.js:69:43) ???at onex (/Users/user/zero-client/node_modules/browser-resolve/node_modules/resolve/lib/async.js:92:31) ???at /Users/user/zero-client/node_modules/browser-resolve/node_modules/resolve/lib/async.js:22:47 ???at FSReqCallback.oncomplete (fs.js:161:21)

发现是在lib/setup-provider.js中调用了metamask-crx模块(metamask chrome extension),但node_modules中没有

从该网址下载https://github.com/ATNIO/metamask-crx:

const MetamaskInpageProvider = require(‘metamask-crx/app/scripts/lib/inpage-provider.js‘)

再运行,还是报错:

Error: Cannot find module ‘brfs‘ from ‘/Users/user/zero-client/node_modules/metamask-crx‘ ???at /Users/user/zero-client/node_modules/resolve/lib/async.js:51:31 ???at processDirs (/Users/user/zero-client/node_modules/resolve/lib/async.js:185:39) ???at ondir (/Users/user/zero-client/node_modules/resolve/lib/async.js:200:13) ???at load (/Users/user/zero-client/node_modules/resolve/lib/async.js:83:43) ???at onex (/Users/user/zero-client/node_modules/resolve/lib/async.js:108:17) ???at /Users/user/zero-client/node_modules/resolve/lib/async.js:12:69 ???at FSReqCallback.oncomplete (fs.js:161:21)

是因为下载下来的metamask-crx模块忘记运行npm install

然后再运行就成功了:

userdeMacBook-Pro:zero-client user$ npm start> metamask-zeroclient@2.0.0 start /Users/user/zero-client> ./example.shbeefy (v2.1.8) is listening on http://127.0.0.1:9001beefy (v2.1.8) is listening on http://127.0.0.1:9002200 ??50ms ??????261B ?/200 ??63ms ????1.95KB ?/bundle.js ? /Users/user/zero-client/node_modules/browserify /Users/user/zero-client/.index.js -d200 2422ms ????3.12MB ?/zero.js ? /Users/user/zero-client/node_modules/browserify /Users/user/zero-client/index.js -d200 ??32ms ??????427B ?/404 ???1ms ???????12B ?/background.js200 1497ms ????2.33MB ?/bundle.js ? ./node_modules/browserify ./frame.js -d200 ??25ms ??????261B ?/200 ?116ms ????1.95KB ?/bundle.js ? /Users/user/zero-client/node_modules/browserify /Users/user/zero-client/.index.js -d200 1720ms ????3.12MB ?/zero.js ? /Users/user/zero-client/node_modules/browserify /Users/user/zero-client/index.js -d200 ???1ms ??????189B ?/favicon.ico (generated)200 ??13ms ??????261B ?/200 ??12ms ????1.95KB ?/bundle.js ? /Users/user/zero-client/node_modules/browserify /Users/user/zero-client/.index.js -d200 1377ms ????3.12MB ?/zero.js ? /Users/user/zero-client/node_modules/browserify /Users/user/zero-client/index.js -d200 ??16ms ??????427B ?/

在没有安装metamask插件的浏览器上的结果是:

在安装了metamask插件的浏览器上的结果是:

看到这个运行的结果后,我们可以看看其的代码:

他的代码与之前的mascara的差不多,如果想要看更详细的解释,可看mascara-2(MetaMask/mascara本地实现)-连接线上钱包

index.js

const Web3 = require(‘web3‘)const setupProvider = require(‘./lib/setup-provider.js‘)//// setup web3//var provider = setupProvider()//1 设置providervar web3 = new Web3(provider)web3.currentProvider = providerweb3.setProvider = function(){ ?console.log(‘MetaMask - overrode web3.setProvider‘)}//// export web3//console.log(web3.currentProvider)// console.log(web3.version.network)// console.log(web3.eth.blockNumber)global.web3 = web3

 zero-client/lib/setup-provider.js

const setupIframe = require(‘./setup-iframe.js‘)const MetamaskInpageProvider = require(‘metamask-crx/app/scripts/lib/inpage-provider.js‘)module.exports = getProviderfunction getProvider(){//1 ?if (global.web3) {//2如果有安装插件,那么就使用插件的web3 ???console.log(‘MetaMask ZeroClient - using environmental web3 provider‘) ???return global.web3.currentProvider ?} ?//3如果没有就自己设置 ?console.log(‘MetaMask ZeroClient - injecting zero-client iframe!‘) ?var iframeStream = setupIframe({ //4设置iframe ???zeroClientProvider: ‘http://localhost:9001‘, //4服务器端为9001 ???sandboxAttributes: [‘allow-scripts‘, ‘allow-popups‘, ‘allow-same-origin‘], ?}) ?var inpageProvider = new MetamaskInpageProvider(iframeStream) //6根据iframe流去设置MetamaskInpageProvider ?return inpageProvider}

zero-client/lib/setup-iframe.js

const Iframe = require(‘iframe‘)const IframeStream = require(‘iframe-stream‘).IframeStreammodule.exports = setupIframefunction setupIframe(opts) {//4 ?opts = opts || {} ?var frame = Iframe({ ???src: opts.zeroClientProvider || ‘https://zero.metamask.io/‘, //5就是如果没有设置服务器,那么就连接zero.metamask.io(docker设置,下面有讲) ???container: document.head, ???sandboxAttributes: opts.sandboxAttributes || [‘allow-scripts‘, ‘allow-popups‘], ?}) ?var iframe = frame.iframe ?var iframeStream = new IframeStream(iframe)//5 然后创建iframe流 ?return iframeStream}

metamask-crx/app/scripts/lib/inpage-provider.js

const pipe = require(‘pump‘)const StreamProvider = require(‘web3-stream-provider‘)const LocalStorageStore = require(‘obs-store‘)const ObjectMultiplex = require(‘./obj-multiplex‘)const createRandomId = require(‘./random-id‘)module.exports = MetamaskInpageProviderfunction MetamaskInpageProvider (connectionStream) {//7 ?const self = this ?// setup connectionStream multiplexing ?var multiStream = self.multiStream = ObjectMultiplex()//8生成多路复用流 ?pipe(//8将多路复用流与iframe流连接 ???connectionStream, ???multiStream, ???connectionStream, ???(err) => logStreamDisconnectWarning(‘MetaMask‘, err) ?) ?// subscribe to metamask public config (one-way) ?self.publicConfigStore = new LocalStorageStore({ storageKey: ‘MetaMask-Config‘ })//8设置本地存储 ?pipe(//8在多路复用流中添加publicConfig流用以接收本地存储信息 ???multiStream.createStream(‘publicConfig‘), ???self.publicConfigStore, ???(err) => logStreamDisconnectWarning(‘MetaMask PublicConfigStore‘, err) ?) ?// connect to async provider ?const asyncProvider = self.asyncProvider = new StreamProvider()//8设置rpc provider流 ?pipe( ???asyncProvider, ???multiStream.createStream(‘provider‘), ???asyncProvider, ???(err) => logStreamDisconnectWarning(‘MetaMask RpcProvider‘, err) ?) ?self.idMap = {} ?// handle sendAsync requests via asyncProvider ?self.sendAsync = function (payload, cb) { ???// rewrite request ids ???var request = eachJsonMessage(payload, (message) => {//9 10 transformFn即这里的回调参数,payload为message ?????var newId = createRandomId()//11这里是将json rpc中的id随机生成一个更复杂的id值去指明该笔request ?????self.idMap[newId] = message.id //11保留旧的id,因为之后返回时返回的还是旧ID,新id只在过程中使用 ?????message.id = newId ?????return message ???}) ???// forward to asyncProvider ???asyncProvider.sendAsync(request, function (err, res) { ?????if (err) return cb(err) ?????// transform messages to original ids ?????eachJsonMessage(res, (message) => { ???????var oldId = self.idMap[message.id] ???????delete self.idMap[message.id] ???????message.id = oldId ???????return message ?????}) ?????cb(null, res) ???}) ?}}MetamaskInpageProvider.prototype.send = function (payload) { ?const self = this ?let selectedAddress ?let result = null ?switch (payload.method) { ???case ‘eth_accounts‘: ?????// read from localStorage ?????selectedAddress = self.publicConfigStore.getState().selectedAddress ?????result = selectedAddress ? [selectedAddress] : [] ?????break ???case ‘eth_coinbase‘: ?????// read from localStorage ?????selectedAddress = self.publicConfigStore.getState().selectedAddress ?????result = selectedAddress ?????break ???case ‘eth_uninstallFilter‘: ?????self.sendAsync(payload, noop) ?????result = true ?????break ???case ‘net_version‘: ?????let networkVersion = self.publicConfigStore.getState().networkVersion ?????result = networkVersion ?????break ???// throw not-supported Error ???default: ?????var link = ‘https://github.com/MetaMask/faq/blob/master/DEVELOPERS.md#dizzy-all-async---think-of-metamask-as-a-light-client‘ ?????var message = `The MetaMask Web3 object does not support synchronous methods like ${payload.method} without a callback parameter. See ${link} for details.` ?????throw new Error(message) ?} ?// return the result ?return { ???id: payload.id, ???jsonrpc: payload.jsonrpc, ???result: result, ?}}MetamaskInpageProvider.prototype.sendAsync = function () { ?throw new Error(‘MetamaskInpageProvider - sendAsync not overwritten‘)}MetamaskInpageProvider.prototype.isConnected = function () { ?return true}MetamaskInpageProvider.prototype.isMetaMask = true// utilfunction eachJsonMessage (payload, transformFn) {//9 ?if (Array.isArray(payload)) {//9如果是数组,说明一次要执行多个request ???return payload.map(transformFn)//9所以将数组中的request一个个传入transformFn,最后传出结果的数组 ?} else { ???return transformFn(payload)//9如果不是,则说明是一个request,直接做transformFn的参数即可 ?}}function logStreamDisconnectWarning(remoteLabel, err){ ?let warningMsg = `MetamaskInpageProvider - lost connection to ${remoteLabel}` ?if (err) warningMsg += ‘\n‘ + err.stack ?console.warn(warningMsg)}function noop () {}

从上面的代码中我们可以看见,如果没有设置provider的话(zeroClientProvider: ‘http://localhost:9001‘),那么就会默认为https://zero.metamask.io/

这是通过docker进行设置的,如下:

 Dockerfile

FROM node:0.12MAINTAINER kumavis# setup app dirRUN mkdir -p /www/WORKDIR /www/# install dependenciesCOPY ./package.json /www/package.jsonRUN npm install# copy over app dirCOPY ./ /www/# run testsRUN npm test# start serverCMD npm start# expose serverEXPOSE 9000

 从这里我们可以看出基本上就是本文档的复制粘贴,部署了一个服务器,并提供了9000作为端口

 docker-compose.yml

zeroClient: ?image: kumavis/zeroclient ?restart: always ?environment: ???VIRTUAL_HOST: "zero.metamask.io" ???VIRTUAL_PORT: "9000" ???PORT: "9000" ?ports: ???- "9000"

 因为该项目的作者已经将该镜像上传,所以我们可以根据 docker-compose.yml的设置直接拉取镜像进行使用:

userdeMBP:zero-client user$ docker-compose upPulling zeroClient (kumavis/zeroclient:)...latest: Pulling from kumavis/zeroclientd4bce7fd68df: Pull completea3ed95caeb02: Pull complete816152842605: Pull complete5dcab2c7e430: Pull completedc54ada22a60: Pull completef4dbf915606d: Pull complete2e6a016344c7: Pull complete310ce789aae0: Pull completed9f04797303f: Pull completee69d1d8ba6cd: Pull complete508a0e5f7217: Pull completeDigest: sha256:6a7936b08541ced92fc35f63033046942df09e6a48a56f199de315eac002be51Status: Downloaded newer image for kumavis/zeroclient:latestCreating zero-client_zeroClient_1 ... doneAttaching to zero-client_zeroClient_1zeroClient_1 ?| zeroClient_1 ?| > metamask-zeroclient@1.0.0 start /wwwzeroClient_1 ?| > node server.jszeroClient_1 ?| zeroClient_1 ?| MetaMask ZeroClient iframe server listening on 9000

然后这时候将zero-client/lib/setup-provider.js中的http://localhost:9001去掉,让其连接https://zero.metamask.io/:

得到结果:

然后在shared-worker文件夹中我们能够看到有:

zero-client/shared-worker/client.js

var worker = new SharedWorker("worker.js");var id = Number.MAX_SAFE_INTEGER*Math.random()//3 随机生成id值worker.port.addEventListener("message", function(e) { ?console.log(e) ?console.log(id) ?console.log("message") ?console.log(e.data);}, false);worker.port.start();//1 激活端口// post a message to the shared web workerworker.port.postMessage(id)//3 发送id,触发server.js的"message"事件

 扩展,参考https://www.jb51.net/html5/551063.html:

SharedWorker类(html5)

  SharedWorker的实质在于share,不同的线程可以共享一个线程,他们的数据也是共享的。

本例子是通过addEventListener()方法监听message事件,需要worker.port.start()方法激活端口。

也有下面这样的写法:

worker.port.onmessage = function(e) { ?console.log(e) ?console.log(id) ?console.log("message") ?console.log(e.data);}

使用事件句柄的方式将听message事件,不需要调用worker.port.start()

不同于worker,当有人和他通信时触发connect事件(如下),然后他的message事件是绑定在messagePort对象上的

zero-client/shared-worker/server.js

var connections = 0 // count active connectionsself.addEventListener("connect", function (e) {//2 进行连接var port = e.ports[0] ?connections++ ?port.addEventListener("message", function (e) {//4 收到消息id = e.data ???port.postMessage("Hello " + e.data + " (port #" + connections + ")")//5 post回消息,然后触发client.js的"message"事件 ?}, false) ?port.start()//2 打开端口}, false)

run example

beefy client.js:index.js server.js:worker.js --live --open 

然后我们为了将这个例子运行起来,需要对package.json进行更改,添加"example":

 ?"scripts": { ???"test": "echo \"Error: no tests yet\" && exit 0", ???"start": "./example.sh", ???"example": "beefy shared-worker/client.js:index.js shared-worker/server.js:worker.js 9003 --live --open " ?},

 这里没有设置端口,会随机使用一个端口,但是你也可以自己设置,这里我们设置为9003,然后运行:

userdeMacBook-Pro:zero-client user$ npm run example> metamask-zeroclient@2.0.0 example /Users/user/zero-client> beefy shared-worker/client.js:index.js shared-worker/server.js:worker.js 9003 --live --open beefy (v2.1.8) is listening on http://127.0.0.1:9003200 ??70ms ??????552B ?/200 ?793ms ????2.36KB ?/index.js ? ./node_modules/browserify ./shared-worker/client.js -d200 ??30ms ??????552B ?/200 ??17ms ????2.36KB ?/index.js ? ./node_modules/browserify ./shared-worker/client.js -d200 ??36ms ??????552B ?/200 ??10ms ????2.36KB ?/index.js ? ./node_modules/browserify ./shared-worker/client.js -d200 ??19ms ??????552B ?/200 ???8ms ????2.36KB ?/index.js ? ./node_modules/browserify ./shared-worker/client.js -d

 并且将index.html上的bundle.js改为index.js:

<!doctype html><html lang="en"><head> ?<meta charset="utf-8"> ?<title>MetaMask ZeroClient Iframe</title> ?<span style="white-space:pre"></span> ?<link href="http://www.lituanmin.com/favicon.ico" rel="icon" type="image/x-icon" /> ?<meta name="description" content="MetaMask ZeroClient"> ?<meta name="author" content="MetaMask"> ?<!--[if lt IE 9]> ?<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script> ?<![endif]--></head><body> ?Hello! I am the MetaMask iframe. ?<script src="/index.js"></script></body></html>

 返回:

 刷新一次可以看见port 4变成了port 5,是第五次访问

 

MetaMask/zero-client

原文地址:https://www.cnblogs.com/wanghui-garcia/p/9890803.html

知识推荐

我的编程学习网——分享web前端后端开发技术知识。 垃圾信息处理邮箱 tousu563@163.com 网站地图
icp备案号 闽ICP备2023006418号-8 不良信息举报平台 互联网安全管理备案 Copyright 2023 www.wodecom.cn All Rights Reserved