# ZKSBT Integration Spec/Workflow/Interface # TODO 8.22 weekly sync: 目标 : NPO : 完成Testnet V1的测试, bug fix, 上Testnet V2, 新增功能 : (1) 用户自付gas fee (2) proof key management(增删) POMP : 开发,集成,下周中上Testnet V1. 1. npo - Testnet V1 : QA Test, Bug Fix - Testnet V2 : - (1) 用户自付gas fee - (2) proof key management 2. pomp - 设计稿 - range : 不同asset的range有产品定义,数量有限,sdk中用string表示,链上用keccak(string) - email project : 增加一次邮箱认证交互(前后端),邮箱信息不上链 - 下周一联调. 下周三提测. 8.15 weekly sync: 目标 : 本周上测试网版本,QA提侧 1. SDK : - generateProofKey 去掉root - backend verify proof. - 上新/升级. (后端规则代码check_eligible, 链上发交易,加mintID/(名字 zkBAB?), 前后端token名字) - 每种pomp是一个SBT Category : POMP-BNB-ETH, POMP-ETH-USDT. attribute调整为range. - [TODO]"sbt category" --> mintID. 2. Backend : - NFT List 接口 - mint立即返回(eligible : true/false) - mint status 接口 (minting, minted, 链上失败?) 3. Frotnend : - 生成proof,查询proof key - NPO/POMP原型变动支持. <!-- - sbt_id 分配放链上? backend_sig 重放? - 增加SBT type/attribute, 是否需要升级合约? --> # zksbt Fee - pulbic mint, privacy verify - user 向backend申请certification,获得 sbt id, certificate signature, proof key. 此时proof key未生效. - user 直接mint,付gas fee,以及项目方fee - backend 同步链上信息, proof key生效. - user 通过proof key manage协议,生成 # Design Goal - 无私钥,根据user签名生成public address - NPO/POMP兼容代码 - 兼容Calamari的一些接口,如mintID含义. - zkSBT由manta server认证, 并提供mint授权 - 拿到授权的任何人,可以mint该zkSBT - Proof Key生成机制开源,可复制, web2项目方可以使用manta的proof key - web2项目方也可以不经过server, 自己认证user的SBT所有权 # Address ||Manta Atlantic|Manta Pacific| |---|---|---| |public address | public address | public address| |private address| zkaddress | eth address| |eth address|eth address|eth address| # SBT Spec <!-- 前端在没有server的情况下, 能够根据链上sbt信息,展示用户所拥有的zkSBT. - 如何区分 zkBAB,zkBadger, POMP等SBT - 链上存储SBT metadata (图片, URL) --> - category : 和mintID定义相同 ```typescript enum SBT_CATEGORY { ZKBAB = 1, ZKKYC = 2, pompETH = 12, pompBNB = 13 ...... } ``` - attribute : 更细粒度的属性,如pomp range ```typescript enum POMP_RANGE { RANGE_0 = "0", RANGE_1 = "1", RANGE_10 = "10", RANGE_100 = "100" } ``` - id : server端认证后分配 <!-- ## 签名消息格式 --> # Proof Key <!-- Motivation : 没有server参与的情况下,第三方是否也能作为验证机构? --> ## mint SBT生成Proof Key mint时,有用户签名,即可生成proof key,无需依赖用户生成zkp proof. ## Proof Key管理(增/删) 需要增/删proof key, 则需要用户证明对zkSBT的所有权,证明方式有二种 : 1. 和mint类似的签名方式 - user 签名 (public_address, sbt_id), 泄漏用户以太坊地址. - backend 验签,生成proof key - 问题 : 纯链下, 有中心化风险, proof key只能由我们自己的server产生 2. 使用zk-proof证明 : - backend/第三方提供随机数salt - user基于salt,生成zk proof - backend 验证zk proof, 并生成proof key = hash(zk-proof, salt) # Workflow 1. Frontend连接钱包,初始化SDK,生成public address ```typescript // 初始化SDK const sdk = await ZKSbtSDK.create(wallet) const public_address = sdk.getPublicAddress() ``` 2. [mint with gas fee] User向Backend发起mint SBT请求 - Frontend签名(public address, category, attribute), 给Backend发Post Certificate ```typescript let category = 12 let attribute = "100" let user_signature = await sdk.claimSbtSignature(category, attribute) Post "Certificate" ``` - backend 从签名中恢复eth_addr, 分配sbt id, 签名(public Address, sbt_id),返回给Frontend ```typescript return signature(public Address, sbt_id) ``` - user发起链上mint交易 ```typescript await sdk.mint(category, attribute, backend_certificate.sbt_id, backend_certificate.signature) ``` - backend 监听到链上event, 生成proof key ```solidity event SbtMinted(uint indexed identity, uint category, string attribute, uint id); ``` 4. [mint without gas fee] 链上mint SBT并得到Proof Key - user要求backend发起链上mint交易(backend_sig, identity, sbt_id) ```typescript const res = await backend.mintAndGetProofKey( sdk.getPublicAddress(), category, attribute, claim_sbt_signature ) const proof_key = res.proof_key ``` - 合约验证backend签名正确,mint sbt_id给到identity 5. user界面查看已经mint到zkSBT(链上查询) 6. user向backend(或第三方希望用zkSBT作为认证)请求增/删zkSBT的proof key - user给参数(identity, sbt_id) - backend 检查sbt_id 已经mint到了identity - backend产生一个随机数salt - user根据salt, 生成zkp proof - user给backend (zkp proof) - backend 验证zkp proof. - backend生成proof key = hash(zk-proof, salt) 7. user 界面zkSBT显示对应的proof key和二维码 ```mermaid sequenceDiagram participant F as User/Frontend participant B as Backend participant C as Contract/Blockchain participant R as Relayer F -->> F : connect_wallet F -->> F : initlize sdk F -->> F : public address = sdk.getPublicAddress() rect rgba(0, 220, 220, .3) %%F -->> B : mint("ETH", ">100") request %% B -->> F : "salt" to prevent atack reply %%F -->> B : sign_mint = sign("mint pomp ETH>100 under $salt") F -->> F : sign_mint_sbt = sign("public_address", "sbt spec : pomp ETH>100") F -->> B : (sign_mint_sbt, public_address, sbt_info) B -->> B : recover eth_addr, check sbt eligible B -->> F : allocate sbt_id ? backend signature(identity, sbt_id) end %% on-chain mint rect rgba(220, 110, 220, .3) F -->> R : mint(backend_sig, identity, sbt_id) R -->> C : mint... C -->> C : verify backend_sig C -->> C : sbt[sbt_id] = identity. end %% Proof Key generate rect rgba(0, 110, 220, .3) F -->> B : generate proof key of (identity, sbt_id) B -->> C : check sbt_id mint to identity B -->> F : salt F -->> B : zk-proof = prove(salt) B -->> B : verify(zkp) B -->> F : proof key = hash(zkp, salt) end ``` # SBT上新 管理员(后端)发交易: - 发交易add(category/attribute, "NAME") ```solidity await contract.addSbt(12, 0, "ZKBAB") ``` Backend/Frontend : - 使用上述定义(category/attribute)来mint/展示sbt # Interface ## [Frontend](https://github.com/Manta-Network/zksbt/blob/integrate/packages/jssdk/src/sdk.ts) ```typescript getPublicAddress : () => bigint; claimSbtSignature : (category : bigint, attribute : string) => Promise<string>; mint : (category : bigint, attribute : string, id : bigint, sig : string) => Promise<void>; generateProof : (category : bigint, attribute : string, root : bigint, salt : bigint) => Promise<Proof>; ``` ## [Backend](https://github.com/Manta-Network/zksbt/blob/integrate/packages/jssdk/src/backend.ts) Post 接口 : - mint - certificate - proof key manage ## Contract ```solidity= // sbt category --> sbt attribute --> public address --> sbt_id mapping(uint => mapping(string => mapping(uint => uint))) public sbt_minted; event SbtMinted(uint indexed identity, uint category, string attribute, uint id); function mint( uint[] calldata identity, uint[] calldata category, uint[] calldata attribute, uint[] calldata ids, bytes[] calldata certificate_signature ); function addSbt( uint category, uint attribute, string calldata name ); function verify( uint category, uint attribute, uint groupId, uint256 nullifierHash, uint256[8] calldata proof ); ``` ## TODO 1. Backend : QuerySbt 性能优化 - 查询/展示用户已经mint的SBT, 目前是直接从链上读取,很慢 - 一般无后端的dapp会用[subgraph](https://thegraph.com/explorer)做链上数据索引 - zksbt有后端,并且mint默认由,因此可以代替subgraph功能,直接做index 2. Backend : mint过程中生成proof key - 现在的workflow,mint和生成proof key是独立的二个步骤 - mintAndGenerateProofKey helper - 默认的salt值 3. Backend : 管理后台 - 上线新的zkSBT(包括pomp新的资产支持) - SBT.createPomp(ASSET.ETH, RANGE.RANGE_100) 4. Backend : SearchProofKey() 5. Frontend V1.5 6. 合约优化 - Sharding Merkle Tree (产研) - 多次mint 同一个pomp,不同的时间. - URL等meta信息上链? 7. 收sbt fee. - public mint, privacy verify zkAddress差异化 SDK发版 合约审计