MetaMask控制器介绍
Metamask的后台控制器实现被设计的高度模块化,充分体现了软件工程中的模块化、安全性和可扩展性原则。在Metamask中拥有非常多的controller(50个左右),每个controller负责实现某个特定的功能,例如:
TransactionController(交易控制器): 负责链上交易的全生命周期管理,包括创建、签名、广播、跟踪状态以及Gas费用估算。
NetworkController(网络控制器): 管理与不同区块链网络的连接与切换,处理RPC端点、网络状态监控以及链ID识别,确保钱包能与各种EVM兼容链正确通信。
KeyringController(密钥管理控制器): 安全管理用户私钥和账户,处理私钥的创建、导入、加密存储以及签名操作。
ApprovalController(批准控制器): 处理需要用户确认的请求(如交易签名、连接DApp),管理批准流程和界面交互。
AccountsController(账户控制器): 管理用户账户信息,包括账户元数据、余额、交易历史和账户切换。
架构详解
控制器框架的设计主要是为了让这些控制器能够:
-
管理自己的状态
-
与其他控制器安全通信
-
实现权限控制
核心组件详解
1. Messenger (消息系统)
Messenger.ts是整个架构的核心通信系统,它提供了两种主要的通信机制:
- Actions (动作): 控制器可以调用的函数
- Events (事件): 控制器可以发布和订阅的消息
export class Messenger<
Action extends ActionConstraint,
Event extends EventConstraint,
> {
// 动作处理器映射
readonly #actions = new Map<Action['type'], unknown>();
// 事件订阅映射
readonly #events = new Map<Event['type'], EventSubscriptionMap<Event>>();
}
Messenger支持:
registerActionHandler: 注册可被其他控制器调用的函数call: 调用其他控制器注册的函数publish: 发布事件给所有订阅者subscribe: 订阅特定类型的事件
2. RestrictedMessenger (受限消息系统)
RestrictedMessenger.ts提供了对Messenger的安全包装,通过命名空间和许可列表实现细粒度的访问控制:
export class RestrictedMessenger<
Namespace extends string,
Action extends ActionConstraint,
Event extends EventConstraint,
AllowedAction extends string,
AllowedEvent extends string,
> {
readonly #messenger: Messenger<ActionConstraint, EventConstraint>;
readonly #namespace: Namespace;
readonly #allowedActions: NotNamespacedBy<Namespace, AllowedAction>[];
readonly #allowedEvents: NotNamespacedBy<Namespace, AllowedEvent>[];
}
每个控制器获得一个受限的Messenger实例,该实例:
- 只允许调用指定的actions和订阅指定的events
- 拥有自己命名空间内actions和events的完全控制权
- 必须遵循命名规则:控制器只能注册/发布形如
controllerName:actionName的actions/events
// 创建TransactionController的受限Messenger
const transactionControllerMessenger = rootMessenger.getRestricted({
name: 'TransactionController',
// 允许调用的外部actions
allowedActions: [
'NetworkController:getNetworkState',
'GasFeeController:getGasFeeEstimates',
'KeyringController:signTransaction'
],
// 允许订阅的外部events
allowedEvents: [
'NetworkController:stateChange',
'AccountsController:selectedAccountChange'
]
});
// 正常工作:这是允许的action
transactionControllerMessenger.call('NetworkController:getNetworkState');
// 错误:未授权action
transactionControllerMessenger.call('PreferencesController:getState');
// 正常工作:这是允许的event
transactionControllerMessenger.subscribe('NetworkController:stateChange')
// 错误:未授权event
transactionControllerMessenger.subscribe('PreferencesController:stateChange')
// 注册自己命名空间下的action - 允许
transactionControllerMessenger.registerActionHandler(
'TransactionController:addTransaction',
(txParams) => this.addTransaction(txParams)
);
// 发布自己命名空间下的事件 - 允许
transactionControllerMessenger.publish(
'TransactionController:transactionAdded',
newTransactionMeta
);
// 错误:尝试注册其他命名空间的action
transactionControllerMessenger.registerActionHandler(
'NetworkController:switchNetwork',
(networkId) => {}
);
// 正确的命名方式
this.messagingSystem.registerActionHandler(
'TransactionController:getState',
() => this.state
);
// 错误的命名方式 - 不符合命名空间规则
this.messagingSystem.registerActionHandler(
'getTransactions',
() => this.getTransactions()
);
这确保了控制器之间的安全隔离,防止未授权访问。
3. BaseController (控制器基类)
BaseControllerV2.ts为所有控制器提供了统一的基础实现:
export class BaseController<
ControllerName extends string,
ControllerState extends StateConstraint,
messenger extends RestrictedMessenger<...>
> {
#internalState: ControllerState;
protected messagingSystem: messenger;
public readonly name: ControllerName;
public readonly metadata: StateMetadata<ControllerState>;
}
BaseController提供了:
状态管理:通过Immer库提供不可变状态更新
Immer库在钱包开发中是非常重要的,因为钱包中的各种数据结构庞大而复杂,并且需要频繁更新,Immer可以大大提高性能和编码效率。
//BaseController的状态管理通过Immer实现真正的不可变状态更新。考虑以下场景:
// 不使用Immer的复杂嵌套状态更新
this.setState({
...state,
transactions: {
...state.transactions,
[txId]: {
...state.transactions[txId],
status: 'confirmed'
}
}
});
// 使用Immer的简洁写法
this.update((state) => {
state.transactions[txId].status = 'confirmed';
});
protected update(
callback: (state: Draft<ControllerState>) => void | ControllerState,
): {
nextState: ControllerState;
patches: Patch[];
inversePatches: Patch[];
} {
const [nextState, patches, inversePatches] = (
produceWithPatches as unknown as (
state: ControllerState,
cb: typeof callback,
) => [ControllerState, Patch[], Patch[]]
)(this.#internalState, callback);
if (patches.length > 0) {
this.#internalState = nextState;
this.messagingSystem.publish(
`${this.name}:stateChange`,
nextState,
patches,
);
}
return { nextState, patches, inversePatches };
}
元数据:指定哪些状态应该持久化、哪些应该匿名化
public readonly metadata: StateMetadata<ControllerState>;
事件通知:状态变更时自动发布事件
this.messagingSystem.publish(`${this.name}:stateChange`, nextState, patches);
流程图:
flowchart TD
A[控制器方法调用] --> B[调用this.update]
B --> C[Immer创建状态草稿]
C --> D[修改草稿状态]
D --> E[Immer生成新状态和补丁]
E --> F[更新内部状态]
F --> G[发布stateChange事件]
G --> H1[其他控制器接收事件]
G --> H2[UI更新视图]
F --> J[检查元数据persist标记]
J --> K[持久化标记为true的状态属性]
K --> L[保存到本地存储]
style A fill:#f9f,stroke:#333,stroke-width:2px
style G fill:#bbf,stroke:#333,stroke-width:2px
style H2 fill:#bfb,stroke:#333,stroke-width:2px
实际控制器实例: TransactionController
从TransactionController.ts中,我们可以看到一个实际的控制器如何利用这个框架:
export class TransactionController extends BaseController<
typeof controllerName,
TransactionControllerState,
TransactionControllerMessenger
> {
constructor(options: TransactionControllerOptions) {
super({
name: controllerName,
metadata,
state: { ...getDefaultTransactionControllerState(), ...options.state },
messenger: options.messenger,
});
// 注册动作处理器
this.messagingSystem.registerActionHandler(
`${controllerName}:updateCustodialTransaction`,
this.updateCustodialTransaction.bind(this),
);
// 调用其他控制器方法
const findNetworkClientIdByChainId = (chainId: Hex) => {
return this.messagingSystem.call(
`NetworkController:findNetworkClientIdByChainId`,
chainId,
);
};
// 订阅事件
this.messagingSystem.subscribe(
'TransactionController:stateChange',
this.#checkForPendingTransactionAndStartPolling,
);
}
#onConfirmedTransaction(transactionMeta: TransactionMeta) {
// 发布事件
this.messagingSystem.publish(
`${controllerName}:transactionConfirmed`,
transactionMeta,
);
}
控制器间通信流程
- 初始化过程:
// 1. 创建根Messenger实例
import { Messenger } from '@metamask/base-controller';
const rootMessenger = new Messenger<ActionType, EventType>();
// 2. 为每个控制器创建RestrictedMessenger实例
const networkControllerMessenger = rootMessenger.getRestricted({
name: 'NetworkController',
allowedActions: ['PreferencesController:getState'],
allowedEvents: ['PreferencesController:stateChange']
});
const transactionControllerMessenger = rootMessenger.getRestricted({
name: 'TransactionController',
allowedActions: ['NetworkController:getNetworkState'],
allowedEvents: ['NetworkController:stateChange']
});
// 3. 初始化各个控制器
const networkController = new NetworkController({
messenger: networkControllerMessenger,
// 其他配置...
});
const transactionController = new TransactionController({
messenger: transactionControllerMessenger,
// 其他配置...
});
- 控制器间调用动作:
class TransactionController extends BaseController<'TransactionController', State, Messenger> {
async submitTransaction(txParams) {
// 获取当前网络状态
const networkState = this.messagingSystem.call(
'NetworkController:getNetworkState'
);
// 使用网络状态信息
const chainId = networkState.provider.chainId;
// 继续交易提交逻辑...
const txMeta = await this.addTransaction({
...txParams,
chainId,
});
return txMeta;
}
}
-
控制器间事件订阅:
NetworkController发布 'NetworkController:stateChange' 事件 TransactionController (已订阅) 接收事件并处理
class NetworkController extends BaseController<'NetworkController', NetworkState, Messenger> {
async setProviderType(type) {
// 更新状态
this.update((state) => {
state.provider = { type, ...getNetwork(type) };
});
// 状态变更事件自动发布
// BaseController会自动执行:
// this.messagingSystem.publish('NetworkController:stateChange', this.state)
}
}
class TransactionController extends BaseController<'TransactionController', State, Messenger> {
constructor({ messenger, ...options }) {
super({
name: 'TransactionController',
metadata,
state: getDefaultState(),
messenger,
});
// 订阅网络变更事件
this.messagingSystem.subscribe(
'NetworkController:stateChange',
this.#onNetworkStateChange.bind(this)
);
}
#onNetworkStateChange(networkState) {
// 当网络变化时,更新所有挂起交易的网络ID
const { chainId } = networkState.provider;
this.update((state) => {
state.transactions.forEach((tx) => {
if (tx.status === 'unapproved') {
tx.chainId = chainId;
}
});
});
}
}
画图表示
graph TB
subgraph "权限控制系统"
RM[RootMessenger]
subgraph "TransactionController权限"
TC[TransactionController]
TC_A[允许的Actions:\nNetworkController:getNetworkState\nKeyringController:signTransaction]
TC_E[允许的Events:\nNetworkController:stateChange\nApprovalController:approved]
TC_O[拥有的命名空间:\nTransactionController:*]
end
subgraph "NetworkController权限"
NC[NetworkController]
NC_A[允许的Actions:\nPreferencesController:getState]
NC_E[允许的Events:\nPreferencesController:stateChange]
NC_O[拥有的命名空间:\nNetworkController:*]
end
TC --> |只能调用允许的Actions| NC
NC --> |只能调用允许的Actions| TC
TC -.-> |只能发布自己命名空间的Events| RM
NC -.-> |只能发布自己命名空间的Events| RM
RM -.-> |只能订阅允许的Events| TC
RM -.-> |只能订阅允许的Events| NC
end
style TC fill:#faa,stroke:#333,stroke-width:2px
style NC fill:#aaf,stroke:#333,stroke-width:2px
style RM fill:#afa,stroke:#333,stroke-width:2px
安全与权限设计
MetaMask的这套架构设计了精细的权限控制:
-
命名空间隔离: 每个控制器只能在自己的命名空间内注册和发布
-
动作许可控制: 每个控制器只能调用被明确允许的动作
allowedActions: ['AccountsController:getState', 'GasFeeController:getGasFeeEstimates']
-
事件许可控制: 每个控制器只能订阅被明确允许的事件
allowedEvents: ['PreferencesController:stateChange']
总结
BaseControllerV2.ts、Messenger.ts和RestrictedMessenger.ts共同构建了一个:
- 模块化: 每个控制器专注于一个功能领域
- 可组合: 控制器可以灵活组合成更大的应用状态
- 类型安全: 利用TypeScript的高级类型系统确保通信安全
- 权限隔离: 精细控制哪些控制器可以访问哪些功能
- 状态管理: 提供不可变状态更新和状态变更通知
这种架构使MetaMask能够构建一个复杂但可维护的钱包应用,同时保持高度的安全性和可扩展性。
学习交流请添加vx: gh313061
下期预告:创建注入网页的Provider