前言
上一节我们介绍了钱包中主要的stream通信组件,这一节我们看他们具体是如何在钱包通信模块大显身手的。 本章涉及到的源码文件地址:
github.com/MetaMask/me... github.com/MetaMask/me... github.com/MetaMask/me... github.com/MetaMask/me... github.com/MetaMask/me...
与钱包通信的几种方式
DApp通过window.ethereum接口通信:
- 网页应用通过注入的Provider API与钱包交互
- 使用EIP-1193标准API请求签名、发送交易等
通过MetaMask UI界面通信:
- 用户通过弹出窗口(popup)、全屏界面或通知直接与钱包交互
- 内部使用trusted communication通道,权限最高
外部连接通信:
- 其他扩展或网页通过chrome.runtime.connect直接连接MetaMask
本章主要讲解DApp通过window.ethereum接口通信,也是最复杂的一种方式,其他通信方式可以大致看成它的子集。
步骤梳理与分析(DAPP到background.js)
这部分是从DAPP通过window.ethereum.request发起请求开始到请求进入background.js,请求在Stream中的流向。注意这不是完整的请求响应过程,把这部分单独拿出来为了让大家参考流程然后对照源码先熟悉一下,后面会给出完整的请求响应流程:
发起请求后的消息流向
1. DApp 发送请求
- DApp 调用
window.ethereum.request({ method: 'eth_requestAccounts' })
- 实际调用的是
MetaMaskInpageProvider
(继承自 StreamProvider
)的 request
方法
- 该方法最终调用
_rpcEngine.handle(payload, callback)
2. 进入 JSON-RPC Engine
_rpcEngine.handle
触发中间件链
- 其中一个中间件是
StreamProvider
构造时注入的 _jsonRpcConnection.middleware
(由 createStreamMiddleware
创建)
3. 通过中间件写入流
_jsonRpcConnection.middleware
通过 stream.push(req)
将请求写入 _jsonRpcConnection.stream
(一个 Duplex 流)
4. pipeline 触发下游流
_jsonRpcConnection.stream
通过 pipeline 连接到 connectionStream
(即 Substream(METAMASK_EIP_1193_PROVIDER)
)
- pipeline 机制会自动调用
Substream._write()
,将请求写入 Substream
5. Substream 写入父 ObjectMultiplex
Substream._write()
实现为:this._parent.push({ name: this._name, data: chunk })
- 这里的 parent 是
ObjectMultiplex
(如 inpage.js 里的 mux
)
- 数据被 push 到
ObjectMultiplex
的缓冲区
6. pipeline 触发 WindowPostMessageStream
ObjectMultiplex
通过 pipeline 连接到 WindowPostMessageStream
(inpage.js 里的 metamaskStream
)
- pipeline 机制会自动调用
WindowPostMessageStream._write()
,将数据写入
7. WindowPostMessageStream 通过 postMessage 发送
WindowPostMessageStream._write()
调用 _postMessage()
,实际通过 window.postMessage
发送数据到目标窗口(内容脚本)
8. contentscript 端 WindowPostMessageStream 接收
- contentscript 端的
WindowPostMessageStream
(如 pageStream
)监听 message
事件,收到数据后调用 _onData
,数据进入流
9. pipeline 触发 ObjectMultiplex
pageStream
通过 pipeline 连接到 ObjectMultiplex
(pageMux
)
- pipeline 机制会自动调用
ObjectMultiplex._write()
,将数据分发到对应的 Substream(pageChannel
)
10. pipeline 触发 extensionEip1193Channel
pageChannel
通过 pipeline 连接到 extensionEip1193Channel
- 数据被写入
extensionEip1193Channel
,再 push 到其父 extensionMux
11. pipeline 触发 PortStream
extensionMux
通过 pipeline 连接到 PortStream
(extensionStream
)
PortStream.write()
通过 Port.postMessage(req)
发送数据到 background.js
12. background.js 端接收
- background.js 端的
Port
监听 onMessage
,收到请求后进入后台处理
流程图表示
flowchart TD
subgraph 网页 [DApp]
A1["window.ethereum.request({ method: 'eth_requestAccounts' })"]
A2["MetaMaskInpageProvider.request()"]
A3["_rpcEngine.handle()"]
A4["_jsonRpcConnection.middleware(req)"]
A5["stream.push(req)"]
end
subgraph inpage.js
B1["Substream(METAMASK_EIP_1193_PROVIDER)"]
B2["ObjectMultiplex (mux)"]
B3["WindowPostMessageStream (metamaskStream)"]
end
subgraph contentscript.js
C1["WindowPostMessageStream (pageStream)"]
C2["ObjectMultiplex (pageMux)"]
C3["Substream(pageChannel)"]
C4["Substream(extensionEip1193Channel)"]
C5["ObjectMultiplex (extensionMux)"]
C6["PortStream (extensionStream)"]
end
subgraph background.js
D1["Port (browser.runtime.onConnect)"]
D2["MetaMask Controller"]
end
%% DApp 到 inpage.js
A1 --> A2 --> A3 --> A4 --> A5
A5 -->|pipeline| B1
B1 -->|push到父| B2
B2 -->|pipeline| B3
B3 -- window.postMessage --> C1
%% contentscript.js 内部流转
C1 -->|pipeline| C2
C2 -->|分发到| C3
C3 -->|pipeline| C4
C4 -->|push到父| C5
C5 -->|pipeline| C6
%% 到 background
C6 -- port.postMessage --> D1
D1 --> D2
%% 说明
classDef stream fill:#f9f,stroke:#333,stroke-width:2px;
classDef logic fill:#bbf,stroke:#333,stroke-width:2px;
class A1,A2,A3,A4,A5 logic;
class B1,B2,B3,C1,C2,C3,C4,C5,C6,D1 stream;
步骤梳理与分析(DAPP请求响应全流程)
请求方向(DApp → background.js)
- DAPP 发送请求(如 eth_requestAccounts)
_rpcEngine.handle(eth_requestAccounts)
- 交给
Duplex::_jsonRpcConnection.middleware
处理
middleware
内部通过 stream.push(req)
写入 Duplex::_jsonRpcConnection.stream
缓冲区
- pipeline 触发
Substream(METAMASK_EIP_1193_PROVIDER)::connectionStream._write()
connectionStream._write()
把请求 push 到父 ObjectMultiplex::mux
缓冲区
- pipeline 触发
WindowPostMessageStream::metamaskStream._write()
metamaskStream._write()
通过 window.postMessage()
发送到 contentscript
- contentscript 的
WindowPostMessageStream::pageStream
接收
- pipeline 触发
ObjectMultiplex::pageMux._write()
,分发到 Substream(METAMASK_EIP_1193_PROVIDER)::pageChannel
- pipeline 触发
Substream(METAMASK_EIP_1193_PROVIDER)::extensionEip1193Channel._write()
extensionEip1193Channel.write()
push 到父 ObjectMultiplex::extensionMux
- pipeline 触发
PortStream::extensionStream._write()
extensionStream.write()
通过 Port.postMessage(req)
发送到 background
- background 端
PortStream::portStream._onMessage()
接收
- 请求传入
metamask-controller.js.setupUntrustedCommunicationEip1193()
- pipeline 触发
ObjectMultiplex::mux.write()
,push 到 Substream(METAMASK_EIP_1193_PROVIDER)::outStream
- pipeline 触发
Duplex::providerStream.write()
engine.handle
最终处理请求,生成响应
响应方向(background.js → DApp)
- 响应 push 到
providerStream
自身缓冲区
- pipeline 触发
Substream(METAMASK_EIP_1193_PROVIDER)::outStream.write()
,写入父 ObjectMultiplex::mux
- pipeline 触发
PortStream::portStream._write()
,响应发送回 contentscript
- contentscript 端
PortStream::extensionStream._onMessage()
接收
- pipeline 触发
ObjectMultiplex::extensionMux.write()
,分发到 Substream(METAMASK_EIP_1193_PROVIDER)::extensionEip1193Channel
- pipeline 触发
Substream(METAMASK_EIP_1193_PROVIDER)::pageChannel.write()
,写入父 ObjectMultiplex::pageMux
- pipeline 触发
WindowPostMessageStream::pageStream._write()
,通过 window.postMessage()
发送到 inpage
- inpage 端
WindowPostMessageStream::metamaskStream
接收
- pipeline 触发
ObjectMultiplex::mux.write()
,push 到 Substream(METAMASK_EIP_1193_PROVIDER)::connectionStream
- pipeline 触发
Duplex::_jsonRpcConnection.stream.write()
,调用 processMessage()
processMessage()
调用 processResponse()
,DAPP 拿到响应结果
流程图表示
flowchart TD
%% DApp -> background 请求链路
subgraph 网页 [DApp]
A1["window.ethereum.request(...)"]
A2["_rpcEngine.handle()"]
A3["_jsonRpcConnection.middleware(req)"]
A4["stream.push(req)"]
end
subgraph inpage.js
B1["Substream(METAMASK_EIP_1193_PROVIDER)::connectionStream._write()"]
B2["ObjectMultiplex::mux"]
B3["WindowPostMessageStream::metamaskStream._write()"]
end
subgraph contentscript.js
C1["WindowPostMessageStream::pageStream"]
C2["ObjectMultiplex::pageMux"]
C3["Substream::pageChannel"]
C4["Substream::extensionEip1193Channel"]
C5["ObjectMultiplex::extensionMux"]
C6["PortStream::extensionStream"]
end
subgraph background.js
D1["PortStream::portStream._onMessage()"]
D2["setupUntrustedCommunicationEip1193()"]
D3["ObjectMultiplex::mux"]
D4["Substream::outStream"]
D5["Duplex::providerStream.write()"]
D6["engine.handle()"]
end
%% background -> DApp 响应链路
subgraph background.js
E1["engine.handle() 生成响应"]
E2["providerStream.push(res)"]
E3["Substream::outStream.write()"]
E4["ObjectMultiplex::mux"]
E5["PortStream::portStream._write()"]
end
subgraph contentscript.js
F1["PortStream::extensionStream._onMessage()"]
F2["ObjectMultiplex::extensionMux"]
F3["Substream::extensionEip1193Channel"]
F4["Substream::pageChannel"]
F5["ObjectMultiplex::pageMux"]
F6["WindowPostMessageStream::pageStream._write()"]
end
subgraph inpage.js
G1["WindowPostMessageStream::metamaskStream"]
G2["ObjectMultiplex::mux"]
G3["Substream::connectionStream"]
G4["Duplex::_jsonRpcConnection.stream.write()"]
G5["processMessage() -> processResponse()"]
G6["DApp 拿到响应"]
end
%% 请求链路
A1 --> A2 --> A3 --> A4
A4 -->|pipeline| B1
B1 -->|push到父| B2
B2 -->|pipeline| B3
B3 -- window.postMessage --> C1
C1 -->|pipeline| C2
C2 -->|分发到| C3
C3 -->|pipeline| C4
C4 -->|push到父| C5
C5 -->|pipeline| C6
C6 -- port.postMessage --> D1
D1 --> D2 --> D3 --> D4 --> D5 --> D6
%% 响应链路
D6 --> E1 --> E2 --> E3 --> E4 --> E5
E5 -- port.postMessage --> F1
F1 --> F2 --> F3 --> F4 --> F5 --> F6
F6 -- window.postMessage --> G1
G1 --> G2 --> G3 --> G4 --> G5 --> G6
%% 说明
classDef stream fill:#f9f,stroke:#333,stroke-width:2px;
classDef logic fill:#bbf,stroke:#333,stroke-width:2px;
class A1,A2,A3,A4,G6 logic;
class B1,B2,B3,C1,C2,C3,C4,C5,C6,D1,D2,D3,D4,D5,D6,E1,E2,E3,E4,E5,F1,F2,F3,F4,F5,F6,G1,G2,G3,G4,G5 stream;
时序图表示
如果不习惯看流程图,可以参考时序图:
sequenceDiagram
participant DApp as DApp
participant Inpage as inpage.js
participant Content as contentscript.js
participant BG as background.js
%% 请求方向
DApp->>DApp: window.ethereum.request(...)
DApp->>DApp: _rpcEngine.handle()
DApp->>Inpage: _jsonRpcConnection.middleware(req)
Inpage->>Inpage: stream.push(req)
Inpage->>Inpage: Substream(METAMASK_EIP_1193_PROVIDER)._write()
Inpage->>Inpage: ObjectMultiplex::mux
Inpage->>Inpage: WindowPostMessageStream::metamaskStream._write()
Inpage->>Content: window.postMessage(req)
Content->>Content: WindowPostMessageStream::pageStream
Content->>Content: ObjectMultiplex::pageMux
Content->>Content: Substream::pageChannel
Content->>Content: Substream::extensionEip1193Channel
Content->>Content: ObjectMultiplex::extensionMux
Content->>Content: PortStream::extensionStream
Content->>BG: Port.postMessage(req)
BG->>BG: PortStream::portStream._onMessage()
BG->>BG: setupUntrustedCommunicationEip1193()
BG->>BG: ObjectMultiplex::mux
BG->>BG: Substream::outStream
BG->>BG: Duplex::providerStream.write()
BG->>BG: engine.handle()
%% 响应方向
BG->>BG: engine.handle() 生成响应
BG->>BG: providerStream.push(res)
BG->>BG: Substream::outStream.write()
BG->>BG: ObjectMultiplex::mux
BG->>BG: PortStream::portStream._write()
BG->>Content: Port.postMessage(res)
Content->>Content: PortStream::extensionStream._onMessage()
Content->>Content: ObjectMultiplex::extensionMux
Content->>Content: Substream::extensionEip1193Channel
Content->>Content: Substream::pageChannel
Content->>Content: ObjectMultiplex::pageMux
Content->>Content: WindowPostMessageStream::pageStream._write()
Content->>Inpage: window.postMessage(res)
Inpage->>Inpage: WindowPostMessageStream::metamaskStream
Inpage->>Inpage: ObjectMultiplex::mux
Inpage->>Inpage: Substream::connectionStream
Inpage->>Inpage: _jsonRpcConnection.stream.write()
Inpage->>Inpage: processMessage() -> processResponse()
Inpage->>DApp: DApp 拿到响应结果
总结
本章有一定的复杂性,需要对照源码去理解,如果还是不理解可以参考对应的视频讲解。
这其实还只是请求响应流的传递过程,实际中间还包括很多RPC中间件的处理过程,控制器间的通信过程,元数据的存取过程,前后端的状态实时同步等等,可见一个好的钱包框架是非常复杂的,不过不用担心,我们后面会一一详细讲解。
学习交流请添加vx: gh313061
本教程配套视频教程:space.bilibili.com/382494787/l...
下期预告:构建json RPC框架