# venus 链服务组件间签名机制
# 背景介绍
venus
的 account
体系中,各角色的关系如下:
sophon-auth
为指定的用户(account
)生成所需权限的token
,SP
用户用此token
访问链服务接口;sophon-auth
保存account-minerIDs
,即一个用户可以拥有对个minerID
;- 链服务组件之间的接口访问没有用到用户的
token
, 是系统内置的共享token
# 用户请求的签名机制
以 sophon-cluster
推送消息的过程为例:
- 以
token
请求sophon-messager
的接口:
PushMessage(ctx context.Context, msg *shared.Message, meta *types.SendSpec) (string, error)
sophon-messager
处理rpcapi
请求,从token
中解析获得在sophon-auth
中生成的account
, 然后调用sophon-gateway
中的签名接口:
WalletSign(ctx context.Context, account string, addr address.Address, toSign []byte, meta venusTypes.MsgMeta) (*crypto.Signature, error)
sophon-gateway
根据account
找到对应的venus-wallet
服务进行签名。
# 链服务内部的签名机制
链服务组件之间的接口访问用的是内置的 share token
,故无法通过 token
解析出 account
, 这样就需要依赖 sophon-auth
的 保存 account-minerIDs
体系。以 venus-miner
请求 sophon-gateway
为例进行说明。
venus-miner
从sophon-auth
获取account-miners
列表;- 先从
account-miners
列表中查找minerID
对应的account
, 然后调用sophon-gateway
的WalletSign
接口。
# 问题分析
本问题以接入 venus
链服务的场景进行分析
- 链服务中没有
signer address
与account
的关系表,而依赖链服务的签名接口需要account
参数,droplet
需要额外扩展signer-account
关系,如下:
假设 f0128788
的可签名地址有: f3wylwd6pclppme4qmbgwled5xpsbgwgqbn2alxa7yahg2gnbfkipsdv6m764xm5coizujmwdmkxeugplmorha
, f3r47fkdzfmtex5ic3jnwlzc7bkpbj7s4d6limyt4f57t3cuqq5nuvhvwv2cu2a6iga2s64vjqcxjqiezyjooq
。
初始 miner-account
, sophon-auth
中实现的关系:
f0128788-test
扩展后,droplet
中的扩展:
f0128788-test
f3wylwd6pclppme4qmbgwled5xpsbgwgqbn2alxa7yahg2gnbfkipsdv6m764xm5coizujmwdmkxeugplmorha-test
f3r47fkdzfmtex5ic3jnwlzc7bkpbj7s4d6limyt4f57t3cuqq5nuvhvwv2cu2a6iga2s64vjqcxjqiezyjooq-test
- 存储市场跟踪订单或扇区状态产生了不必要的工作量,会把非
miner Actor
的地址也进行遍历,如下:
func (dealTracker *DealTracker) scanDeal(ctx metrics.MetricsCtx) {
addrs, err := dealTracker.minerMgr.ActorAddress(ctx)
if err != nil {
log.Errorf("get miners list %w", err)
}
head, err := dealTracker.fullNode.ChainHead(ctx)
if err != nil {
log.Errorf("get chain head %w", err)
}
for _, addr := range addrs {
dealTracker.checkSlash(ctx, addr, head.Key())
dealTracker.checkPreCommitAndCommit(ctx, addr, head.Key())
}
}
# Specification (feature Spec)
本次重构涉及组件包括:sophon-auth,sophon-gateway,sophon-messager, venus-miner, droplet等,接下来将一一介绍修改内容。
# sophon-auth
sophon-auth
提供接口将签名地址和账号进行绑定,并提供查询接口,保障链服务可以安全地支持多用户相互签名,接口如下:
type IAuthClient interface {
GetUserBySigner(signer string) (auth.ListUsersResponse, error)
RegisterSigners(user string, addrs []string) error
UnregisterSigners(user string, addrs []string) error
}
# sophon-gateway
签名接口重构:
type IWalletClient interface {
WalletHas(ctx context.Context, addr address.Address, accounts []string) (bool, error) //perm:admin
WalletSign(ctx context.Context, addr address.Address, accounts []string, toSign []byte, meta types.MsgMeta) (*crypto.Signature, error) //perm:admin
}
sophon-gateway
签名接口只开放对具有admin
权限的token
访问,也就是只能被链服务内部访问,如:venus-miner
,sophon-messager
;- 在调用
sophon-gateway
的签名接口前需要完成权限的检查,这样做为了避免构造消息,调用别人账号下的私钥签名; - 在调用
sophon-gateway
的签名接口时需要带上密钥绑定的账号,这样做是为了多个账号的venus-wallet
可以相互签名;
自动注册/注销签名地址:
- 在
venus-wallet
建立连接时将其保存的私钥和支持的账号进行校验,通过后调用接口保存到sophon-auth
。如 某个venus-wallet
终端管理的私钥有: w1,w2;配置的支持账号有:user01,user02;那么绑定的关系有: user01-w1,user01-w2,user02-w1,user02-w2; - 在
venus-wallet
连接端口或通过接口删除某个地址时自动注销签名地址和账号的绑定关系。
# sophon-messager
删除强制推送消息的接口,SP统一消息推送接口。
type IMessager interface {
- ForcePushMessage(ctx context.Context, account string, msg *types.Message, meta *mtypes.SendSpec) (string, error) //perm:admin
- ForcePushMessageWithId(ctx context.Context, id string, account string, msg *types.Message, meta *mtypes.SendSpec) (string, error) //perm:write
}
推送消息流程修改:
- SP组件调用链服务通常是 [
sign
,read
,write
] 权限的token
,sophon-messager
首先要检查接口带的token
权限是否满足:token
对应的的账号与消息中的签名地址msg.From
是绑定的;token
具有sign
权限(待定);
- 给签名接口提供签名地址绑定的所有账号,这样只要有一个连接到
sophon-gateway
的签名终端(venus-wallet
)支持对此密钥签名则可以签名此条消息,也就是多个账号相互帮助签名;
# venus-miner
无逻辑变化,只需匹配新的接口调用即可。
# droplet
- 取消额外扩展
signer-account
关系的逻辑; - 消息签名前先检查请求
token
的权限,通过后调用sophon-gateway
的签名接口。
这里要注意的是 droplet-client
组件,其实现和 droplet
在同一项目,但是其本质是SP类组件,过程中的签名不能通过sophon-gateway
, 其签名逻辑:
- 对于deals的签名需要额外的签名终端(
venus-wallet
或lotus/venus
); - 交易过程中的消息推送到
sophon-messager
, 和sophon-cluster
等推送消息一致;
所以
droplet-client
组件代码独立为一个项目比较好?从droplet
中剥离?