Hengxin Wallet
恒信简介
Hengxin Network是使用有向无环图实现的分布式账簿系统。在Hengxin Network中,每一次记账行为就是往主网上发送了一次Transaction;主网将为每一个被确认了的Transaction生成一个快照Snapshot。基于Snapshot,各个节点构建了有向无环图,从而实现了高效,低功耗的分布式账簿系统。
与其他区块链项目不同,Hengxin Network从一开始就兼顾了隐私保护。Hengxin Network上的地址都包含三对公私钥:Private View Key / Public View Key, Private Spend Key / Public Spend Key, Private Encrypt Key / Public Encrypt Key。其中, Public View Key, Public Spend Key 以及 Public Encrypt Key构成了Address。只有持有了相应的私钥,才可以分辨出指向该地址的记录:
- 若持有某Address,则可以向该Address发送Transaction
- 若持有某Address的Private View Key,则可以识别出链上所有指向该Address的UTXO
- 若持有某Address的Private View Key 以及 Private Spend Key,则可以消费与该Address相关的UTXO
另外,Hengxin Network中亦引入了Asset概念。实际使用中,可以使用不同的Asset来区分不同的业务方。
链上数据
Hengxin Network中,raw data(用户数据,可以为图片,语音,文字等任何数据)将加密存储于Data Provider中;链上将只存储data hash数据。
数据上链流程:
当用户A (AddrA) 需要向用户B (AddrB), 用户C (Addr C) 生成Transaction,发送数据D时,
- 用户A选取加密算法,并随机生成加密密钥,生成加密密钥明文信息K ({"alg":"SM4","key":"xxxx"})
- 用户A使用加密算法对明文数据D进行加密,生成数据密文d
- 用户A分别使用用户A,B,C的Public Encrypt Key对加密密钥明文信息K进行加密,得到加密密钥密文数据组M ({Addr_A:"K_encrypted_by_A",Addr_B:"K_encrypted_by_B",Addr_C:"K_encrypted_by_C"})
- 用户A将数据密文K与加密密钥密文数据组M发送至Data Provider
- 用户A生成Memo: {"h":"data_hash","exp":123}
- 用户A生成Transaction,指向[Addr_A, Addr_B, Addr_C],完成数据上链
数据流程:
- 用户A同步链上Snapshot,得到Transaction,解析出Memo信息
- 用户使用data_hash在Data Provider中读到数据密文d 以及 加密密钥密文数据
- 用户使用Private Encrypt Key对加密密钥密文数据进行解密,得到加密密钥明文信息K
- 用户使用加密密钥明文信息K对数据密文d进行解密,得到明文数据
编译
go install github.com/fox-one/hxwallet
运行
复制config.template.yaml到config.yaml,并适当配置。
Setup DB:
./hxwallet setdb --config config.local.yaml
Create User:
./hxwallet createuser --config config.local.yaml
指令将有如下输出:
user id: 6e5afa15-eb51-3d58-87cb-70dda3231df0
address: HXvrMpQKfFNTnLFSuGULugcsLxxxMWV3qBUnWc2HH81RhAdXKtUNxFnfoN7qXaNqs6LGgx1JL6ry1nd9sKjMDoRFJUjw6QRLJW9D7meogsdYHjWYEZKWVJzbMN6v5wYHxYnraimXzebPt772279NFQe6mWBFWwytTwU9mWxBXBmXV1JywPewNmZzGVYG8vHa8ySrbss9oU6Eso5CpG13rdJeV32
keystore: HXKSxUSRLudmJ4yxr7aC6Nq4Q1RPVim9woufmxyUGmvWcMizDshdvBiqWq54qj6TX7K592xzoy1vyrhRodnqSwGQzH4ZW7gKaF3K4EqNy8UNUo6DfsZoLzRH8jwP9e5K7xT316zX1zNSmmcfCobUJ4CfFR44poW3VwZweUFzwvGiMmkqGhpm5gKyXN53vaqAciVReBFzfEdEyAQiBm5oFbTfB5kBz1bUFJ3BJbfz1Ac6MauFpzotToYJD85fDCtxfEKekj
此账号作为系统账号,将address提供给kernel运行方,运行方将会提供足够的balance以使得系统可以正常运转。
Worker:
./hxwallet worker --debug --config config.local.yaml
Server:
./hxwallet server --debug --config config.local.yaml --port 8082
API
Auth
Hengxin Wallet实现了两种鉴权,
- Basic: "Authorization: Basic base64(user_id)"
- JWT: "Authorization: Bearer jwt_token"
若配置文件中开启了verify_auth_token, 则必须使用Bearer方式;否则两种皆可。
JWT Token
JWT Header:
{
"alg": "SM2",
"typ": "JWT"
}
JWT Payload:
{
"exp": 1584064554, // 此 token过期时间。Unix Timestamp, 以s为单位
"iat": 1583978154, // 此 token签发时间,为当前时间即可。Unix Timestamp, 以s为单位
"iss": "HXy9YaTXHcNcQUXAHS2sBFX2A8yd2zEiePL4Bo5ZmwRGtgXbtnv142aja1Qw4k8m1pWfPaSS4cddx8GjDDdUFV5hgHZT1M5VrRBVsiiGb4iU953i9M9tKhUXWtZFKKZHaNWNnAiMnsVHUifyubY5Mm5PtqwJDNNzU2hYt1Mtqvhcd8ZnJwBeNygvgjicnBajr52ZjKPtd85bezMnVUFCrK4iUDn", // 根用户的 address
"nc": "__uuid__",
"sig": "xxxx", // optional. request signature,sm3.hash(METHOD+URI+BODY),注意,URI不包含host,例如sms.hash("GET/snapshots")。若Wallet未开启verify_request_signature,则"sig"可以唯恐
}
生成JWT Token Demo:
func GenerateToken(ekInPEM, address string, expires int64) (string, error) {
block, _ := pem.Decode([]byte(ekInPEM))
if block == nil {
return "", errors.New("failed to decode private key")
}
ek, err := sm2.ParsePKCS8PrivateKey(block.Bytes, nil)
if err != nil {
return "", err
}
header := `{"alg":"SM2","typ":"JWT"}`
iat := time.Now().Unix()
payload := fmt.Sprintf(`{"exp":%d,"iat":%d,"iss":"%s"}`, iat+expires, iat, address)
signedData := strings.Join([]string{
strings.TrimRight(base64.URLEncoding.EncodeToString([]byte(header)), "="),
strings.TrimRight(base64.URLEncoding.EncodeToString([]byte(payload)), "="),
}, ".")
hasher := sm3.New()
hasher.Write([]byte(signedData))
sig, err := ek.Sign(rand.Reader, hasher.Sum(nil), nil)
if err != nil {
return "", err
}
return strings.Join([]string{
signedData,
strings.TrimRight(base64.URLEncoding.EncodeToString([]byte(sig)), "="),
}, "."), nil
}
User INFO
GET /info
Response:
{
"code": 0,
"data": {
"user_id": "6c54d04f-585a-3c0b-869f-6e63f6912410",
"created_at": "2020-03-12T02:15:11Z",
"address": "HX266NGnUZ5HmXzzgBW4C1asDcPGqxteTJZu9EaoFUGLxT6KZEcH96bNE1L4zqy3neBbjdZLScbr1vMM8rB186qrHFERetcwY36ecxW1yToJG3MZ6gw3Pf4oGx7NyELanCzNWbnRL8NmjoRQsuUtPKEgXegoq5oh6yyJ5y9prH43gTj7zjYxgkkcvnGj9osFkKb9vk6q4nDGL5vtpa63kUjV6oSe"
}
}
Create User
POST /users
Response:
{
"code": 0,
"data": {
"user_id": "6c54d04f-585a-3c0b-869f-6e63f6912410", // 在访问该用户资源(如snapshot列表)时使用,通过user_id完成用户鉴权
"created_at": "2020-03-05T22:21:13.321112+08:00",
"address": "HX266NGnUZ5HmXzzgBW4C1asDcPGqxteTJZu9EaoFUGLxT6KZEcH96bNE1L4zqy3neBbjdZLScbr1vMM8rB186qrHFERetcwY36ecxW1yToJG3MZ6gw3Pf4oGx7NyELanCzNWbnRL8NmjoRQsuUtPKEgXegoq5oh6yyJ5y9prH43gTj7zjYxgkkcvnGj9osFkKb9vk6q4nDGL5vtpa63kUjV6oSe", // 在生成Transaction时使用
"keystore": "HXKS7ET32Wgs4JntsxqewKMPQTR19EAxjFtxMhVvUMAP6uu2nxYZUtLgJMmNA6nv1Jr7eVWdEJxSrn1ggjVjEYarPEKpiogyNvHrLKX5sZcnsdeYYgYT2uK9KZJayNHJ3u7bJfSuKB7ZoXcPMst8fuSCMPjmFLq5h3dN8i6pLK2RFVuXbAYiAsDfxrPK7aiXZgq71u8FxEgszVjZn2HBezVFtUmy6VhdRcNSYgnNevmhiKWYG4aVpJtFDQ1xJi6yWnaENh"
}
}
提交 Transaction
POST /transactions
Param:
{
"asset": "b9f49cf777dc4d03bc54cd1367eebca319f8603ea1ce18910d09e2c540c630d8",
"opponent_addresses": ["HX266NGnUZ5HmXzzgBW4C1asDcPGqxteTJZu9EaoFUGLxT6KZEcH96bNE1L4zqy3neBbjdZLScbr1vMM8rB186qrHFERetcwY36ecxW1yToJG3MZ6gw3Pf4oGx7NyELanCzNWbnRL8NmjoRQsuUtPKEgXegoq5oh6yyJ5y9prH43gTj7zjYxgkkcvnGj9osFkKb9vk6q4nDGL5vtpa63kUjV6oSe"], // 指向用户Address,银行Address,同时还可以添加其他监管机构Address,如金融局地址
"trace_id": "6c54d04f-585a-3c0b-869f-6e63f6912410", // 任意字符串,最大长度为36。在Hengxin Wallet内唯一,从而保证一个Transaction只能被记录一次。实际使用中,可以根据业务的信息来生成,这样在请求失败之后可以重复提交保证数据上链成功,而又不会发生重复上链的事件
"memo": "{\"t\":\"reg\",\"d\":\"{}\",\"h\":\"xxxx\"}" // 链上数据,此数据为公开可见数据,不建议添加隐私数据
}
Response:
{
"code": 0,
"data": {
"trace_id": "6c54d04f-585a-3c0b-869f-6e63f6912410",
"tx_hash": "2d259a9cbe49eccd7878112e291e378fee7c08af0b443c598b1fbc091d7345fc"
}
}
Inner Transfer
POST /transfer
Param:
{
"asset": "b9f49cf777dc4d03bc54cd1367eebca319f8603ea1ce18910d09e2c540c630d8",
"opponent_id": "c95a14b8-14fe-3228-aa2b-a28c88bd1591", // transaction接收方user_id
"trace_id": "6c54d04f-585a-3c0b-869f-6e63f6912410", // 任意字符串,最大长度为36。在Hengxin Wallet内唯一,从而保证一个Transaction只能被记录一次。实际使用中,可以根据业务的信息来生成,这样在请求失败之后可以重复提交保证数据上链成功,而又不会发生重复上链的事件
"memo": "{}"
}
Response:
{
"code": 0,
"data": {
"trace_id": "6c54d04f-585a-3c0b-869f-6e63f6912410",
"tx_hash": "2d259a9cbe49eccd7878112e291e378fee7c08af0b443c598b1fbc091d7345fc"
}
}
Read Snapshots
读取 记账记录列表
GET /snapshots
Param:
asset = b9f49cf777dc4d03bc54cd1367eebca319f8603ea1ce18910d09e2c540c630d8 # 可选,若为空则查询所有
from = 1 # 上一条snapshot深度。ASC时,查询height > from的数据; DESC时查询height < from的数据
limit = 1 # 返回数据条目。最大 500
order = ASC / DESC
Response:
{
"code": 0,
"data": [
{
"created_at": "2020-03-05T13:17:37Z",
"asset": "b9f49cf777dc4d03bc54cd1367eebca319f8603ea1ce18910d09e2c540c630d8",
"user_id": "c852a70d-86ca-3832-b7ab-bbfdf68bc39b",
"opponent_id": "c95a14b8-14fe-3228-aa2b-a28c88bd1591",
"amount": "0.00000001", // 正数时为用户接收到transaction;负数时为用户发起的transaction
"trace_id": "6c54d04f-585a-3c0b-869f-6e63f6912410", // uuid, 在Hengxin Wallet内唯一
"memo": "{}",
"height": 100 // Hengxin Wallet中的snapshot深度, 下次查询时
}
]
}