不给私钥也能代你发消息:Cosmos SDK 的 x/authz 授权怎么发、怎么过期 图 1
不给私钥也能代你发消息:Cosmos SDK 的 x/authz 授权怎么发、怎么过期 · 图 1

不给私钥,怎么让别人替你发消息

钱包被盗最常见的形态是私钥外泄:只要钥匙交出去,账户里的每一类资产、每一种动作就全部交出去了。Cosmos SDK 的 x/authz 模块提供了一条不同的路:把”发送某一类消息”的权利单独授予另一个账户,授权本身写在链上,私钥不用离开本机。官方文档对它的定位很直白——按 ADR-030 实现,允许一个账户(granter,授权人)把任意权限授予另一个账户(grantee,被授权人),且授权必须逐条绑定在某个具体的消息服务方法上。也就是说,authz 里没有”什么都可以干”的总授权,每一张授权只覆盖一类交易。

一笔授权由什么构成

官方文档给出的存储结构能说明设计粒度:一笔授权由授权人地址、被授权人地址和授权类型三者共同标识,同一个三元组只允许存在一张授权;想延长或改条件,就再发一笔 MsgGrant 覆盖旧的。执行时,被授权人提交 MsgExec,把原本要执行的消息包在里面带上链;节点查表找到对应授权,调用该授权类型的校验逻辑,通过后才以授权人的身份执行。官方文档同时列出几类入表失败的条件:授权人与被授权人是同一个地址、给出早于当前时间的过期时间、或者被授权的消息类型在应用路由里没有对应处理程序——第三种情况说明 authz 不是万能代理,链上根本没注册的消息类型授不了权。这意味着两件事:手续费由发起执行的被授权人支付,而资金变动记在授权人账上——被授权人永远动不了自己没被授权的那部分能力。

授权人把带额度与名单约束的一类消息权限授给被授权人执行的示意

三种内置授权与它们的约束面

SDK 自带三种实现。SendAuthorization 针对转账消息,带一个 spend_limit(额度上限),文档明确这个额度会随着花费递减,还能附带可选的 allow_list,限定只能向名单内地址发送;StakeAuthorization 把委托、赎回、重新委托拆成三种单独授权的类型,可选 MaxTokens 限制数量,还能给出允许或禁止的验证者名单——为了防止拒绝服务攻击,SDK 会遍历这两个名单并按名单里的验证者逐个计收 gas;GenericAuthorization 最宽,只写一个消息类型 URL,授权被授权人不受额外限制地执行该类型消息。选哪种授权,关键看有没有额度、名单这类可核字段可用:能给额度的用额度,只能给 generic 的要想清楚这类消息本身能触及哪些资产。

过期、撤销与容易踩的坑

授权可以带过期时间,不填则不自动到期。链上维护一张按过期时间排列的授权队列,每个区块结束时检查并清理已过期的记录;MsgExec 在碰到过期授权时也会顺带把它删掉。但”过期”不等于”立刻失效”——清理发生在区块尾部的修剪流程里,判断某一刻授权是否可用,应以过期时间戳本身为准,而不是猜修剪有没有跑。撤销走 MsgRevoke,按消息类型 URL 精确撤销;重复 MsgGrant 同一三元组是更新授权的正规做法。官方文档还列出几类必然失败的情形:授权人与被授权人填了同一个地址、给了过去的过期时间、或者授权对应的消息类型根本没注册处理程序。

和 EVM 世界的授权不是一回事

第一个常见混淆是模块名:x/authz 负责授权代执行,x/auth 负责账户与交易签名这两件基础工作,名字只差两个字母。第二个混淆是跨生态类比:EVM 世界的 approve/permit 管的是代币合约里的 allowance 额度,作用范围由那一张代币合约定义;authz 授权的对象是”一类链上消息”,可以是转账、质押、投票、治理,覆盖面由消息类型 URL 决定,执行者也是另一个独立账户而非合约。给机器人或子账户分配职责时,前者是”允许合约动用我多少代币”,后者是”允许这个地址替我发这种交易”——权限模型不同,清点与撤销的动作自然也要分开做。

风险提示:本文为机制说明,不构成投资建议;内置授权类型、字段与计费细节会随 Cosmos SDK 版本变化,动手前请以官方模块文档为准。