# 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发版
合约审计