sdk-go 支持使用在专门私钥托管kms签名返回结果构建和发送交易
【提议方】选填
组织/单位/公司/个人的名称/昵称
【需求分类】
- [] chainmaker-go 新特性
- bugfix
- 新项目
- 其他 sdk-go v2.3.1
其他:
【功能/项目名称】
具体的功能名称或者项目名称,方便以后交流。
【开发周期】
人天
【适配版本】
拟适配/合入的chainmaker-go的版本,要求大版本向前兼容
【功能/项目描述】
具体所要实现的功能详细描述(需求说明、设计文档、如何实现、开发周期等)
目前在开发过程中,发现sdk-go支持构建交易支持方法模式:
- 直接使用和链建立连接的客户端用户私钥签名发送交易
- 使用一对已知本地私钥公钥签名发送交易。
以上2种满足不了我现在开发需求,我的需求是本地构建好交易payload,然后将payload发送给私钥托管服务器签名返回签名结果,然后本地再使用签名结果构建交易。 本需求优点:sdk-go本地不需要知道用户私钥就依赖私钥托管服务器签名交易,保证用户密钥的安全
【考虑过的其他实现方案】
列举其他实现方案,并对比分析。 我在这里理解目前sdk-go方法
- 直接使用和链建立连接的客户端用户私钥签名发送交易(sdk-go 目前支持):对应代码如下
func SafeMintClientSigner(blockchainReq *types.InvokeCommonReq) (status int64, txId string, err error) {
......
contractName := "WDF1"
method := "safeMint"
address := userEthAddr
tokenURI := safeMintParams.TokenURI
dataByte, err := myAbi.Pack(method, address, tokenURI)
if err != nil {
logx.Errorf("abi pack invoke contract param error %v", err)
return -1, "", errors.New("abi pack invoke contract param error")
}
dataString := hex.EncodeToString(dataByte)
kvs := []*common.KeyValuePair{{
Key: "data",
Value: []byte(dataString),
}}
resp, err := chainClient.InvokeContract(contractName, method, txId, kvs, -1, false)
if err != nil {
logx.Errorf("client invokeContract to blockchain node failed contractName:%v, method:%v, txId:%v, kvs:%v, error:%v", contractName, method, txId, kvs, err)
return -1, txId, err
}
}
- 使用一对已知本地私钥公钥签名发送交易(sdk-go 目前支持):对应代码如下。
func SafeMintBySigner(blockchainReq *types.InvokeCommonReq) {
.....
contractName := "WDF1"
method := "safeMint"
address := userEthAddr
tokenURI := safeMintParams.TokenURI
dataByte, err := myAbi.Pack(method, address, tokenURI)
if err != nil {
logx.Errorf("abi pack err %v", err)
return -1, "", err
}
dataString := hex.EncodeToString(dataByte)
kvs := []*common.KeyValuePair{{
Key: "data",
Value: []byte(dataString),
}}
//
vkOrg1Client2 := `-----BEGIN PRIVATE KEY-----
MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgfDG3+6qOZAxugx6d
egYRXfQ3gRTTmOMnq7NXgNoAqpqgCgYIKoEcz1UBgi2hRANCAAS32zqeAK+tLlHh
eQOqwa0+lN7J9DZXG+tbCcSBXecSUio7Cw6kBlfTCig+1TYQG2H9iD3sa1X7OaMc
zcq5Rtpl
-----END PRIVATE KEY-----`
org1CrtClient2 := `-----BEGIN CERTIFICATE-----
MIICcDCCAhagAwIBAgIDA1FXMAoGCCqBHM9VAYN1MIGGMQswCQYDVQQGEwJDTjES
MBAGA1UECBMJR3Vhbmd6aG91MRIwEAYDVQQHEwlHdWFuZ3pob3UxGzAZBgNVBAoT
EnhmLW9yZzEueHVuZmVuZy5jbjESMBAGA1UECxMJcm9vdC1jZXJ0MR4wHAYDVQQD
ExVjYS54Zi1vcmcxLnh1bmZlbmcuY24wHhcNMjMwNjI4MDIxMTIzWhcNMjgwNjI2
MDIxMTIzWjCBjTELMAkGA1UEBhMCQ04xEjAQBgNVBAgTCUd1YW5nemhvdTESMBAG
A1UEBxMJR3Vhbmd6aG91MRswGQYDVQQKExJ4Zi1vcmcxLnh1bmZlbmcuY24xDzAN
BgNVBAsTBmNsaWVudDEoMCYGA1UEAxMfY2xpZW50Mi5zaWduLnhmLW9yZzEueHVu
ZmVuZy5jbjBZMBMGByqGSM49AgEGCCqBHM9VAYItA0IABLfbOp4Ar60uUeF5A6rB
rT6U3sn0Nlcb61sJxIFd5xJSKjsLDqQGV9MKKD7VNhAbYf2IPexrVfs5oxzNyrlG
2mWjajBoMA4GA1UdDwEB/wQEAwIGwDApBgNVHQ4EIgQggwcV9jiAKZWR5SY1goBT
DIzZPYwDmJtzxlLh36e4j+MwKwYDVR0jBCQwIoAgbr4VzktxCf7Xf4Qdx9hU4ab8
PHvn7ictil4ojDMc7yAwCgYIKoEcz1UBg3UDSAAwRQIgKI5q5WY4YOwnw5beiM1p
4tqea76rM8UA1EZV+AUH4JICIQDiJTg6iBae/6+FQ5jM0DJyLELgoc4b/bCsnLwX
AAd1yQ==
-----END CERTIFICATE-----`
privateKey, err := asym.PrivateKeyFromPEM([]byte(vkOrg1Client2), []byte(""))
if err != nil {
logx.Errorf("parse private %v", privateKey)
return -1, "", err
}
cert, err := sdkutils.ParseCert([]byte(org1CrtClient2))
if err != nil {
logx.Errorf("parse certPem err %v", cert)
return -1, "", err
}
signer1 := &sdk.CertModeSigner{
PrivateKey: privateKey,
Cert: cert,
OrgId: "xf-org1.xunfeng.cn",
}
resp, err := chainClient.InvokeContractBySigner(contractName, method, txId, kvs, -1, false, nil, signer)
if err != nil {
logx.Errorf("client invokeContract by signer to blockchain node failed contractName:%v, method:%v, txId:%v, kvs:%v, signer:%v, error:%v", contractName, method, txId, kvs, signer, err)
return -1, txId, err
}
}
- 我的需求是本地构建好交易payload,然后将payload发送给私钥托管服务器签名返回签名结果,然后本地再使用签名结果构建交易(建议新添加方法)
func SafeMintByPrivateTrustServerUserSigner(blockchainReq *types.InvokeCommonReq) (status int64, txId string, err error) {
......
method := "safeMint"
address := userEthAddr
tokenURI := safeMintParams.TokenURI
dataByte, err := myAbi.Pack(method, address, tokenURI)
if err != nil {
logx.Errorf("abi pack invoke contract param error %v", err)
return -1, "", errors.New("abi pack invoke contract param error")
}
dataString := hex.EncodeToString(dataByte)
kvs := []*common.KeyValuePair{{
Key: "data",
Value: []byte(dataString),
}}
payload := chainClient.CreatePayload("", common.TxType_INVOKE_CONTRACT, contractName, method, kvs, 0, nil)
encodePayload, err := proto.Marshal(payload)
if err != nil {
logx.Errorf("invoke payload proto marshal error %v", err)
return -1, "", errors.New("invoke payload proto marshal error")
}
resp, err := l.svcCtx.PrivateTrustClient.Sign(l.ctx, &rpc.Request{
Did: did,
PayloadWaitSign: encodePayload,
})
if err != nil {
metrics.CallPrivateTrustServerErrCount.Inc("sign")
logx.Errorf("private trust server sign contract tx payload error %v", err)
return -1, "", errors.New("private trust server sign contract tx payload error")
}
member := &accesscontrol.Member{}
err = proto.Unmarshal(resp.Member, member)
if err != nil {
logx.Errorf("invoke access control Unmarshal member error %v", err)
return -1, "", errors.New("invoke access control Unmarshal member error")
}
signer2 := &blockchain.Signer{
Signer: member,
Signature: resp.Signature,
}
status, txIdRes, err := chainClient.InvokeContractByPayloadSigner(payload, -1, false, signer2)
if err != nil {
logx.Errorf("InvokeUserContractNoSyncResult error %v", err)
return status, txIdRes, err
}
return status, txIdRes, nil
}
//新添加方法,命名有待优化
func (cc *ChainClient) InvokeContractByPayloadSigner(payload *common.Payload, timeout int64, withSyncResult bool, signer Signer) (*common.TxResponse, error) {
//
//cc.logger.Debugf("[SDK] begin InvokeContractBySigner, [contractName:%s]/[method:%s]/[txId:%s]/[params:%+v]",
contractName, method, txId, kvs)
construct payload
payload := cc.CreatePayload(txId, common. TxType_INVOKE_CONTRACT, contractName, method, kvs, defaultSeq, limit)
Construct TX Req & Sign
req, err := cc.GenerateTxRequestBySigner(payload, nil, signer)
if err != nil {
return nil, err
}
send tx req
resp, err := cc.sendTxRequest(req, timeout)
if err != nil {
return resp, fmt. Errorf("send %s failed, %s", payload. TxType.String(), err. Error())
}
if resp. Code == common. TxStatusCode_SUCCESS {
if withSyncResult {
r, err := cc.getSyncResult(payload. TxId)
if err != nil {
return nil, fmt. Errorf("getSyncResult failed, %s", err. Error())
}
resp. Code = r.Result.Code
resp. Message = r.Result.Message
resp. ContractResult = r.Result.ContractResult
resp. TxId = payload. TxId
resp. TxTimestamp = r.TxTimestamp
resp. TxBlockHeight = r.TxBlockHeight
}
}
return resp, nil
}
type Signer struct {
Signer accesscontrol. Member
Signature []byte
}
func(s *signer) Sign(payload *common. Payload) (signature []byte, err error) {
return s.Signature, nil
}
func(s *Signer) NewMember() (*accesscontrol. Member, error) {
return s.Signer, nil
}
【预计影响范围】
描述该功能可能的影响范围,方便评估。 若是新项目则可忽略 预计不会产生大影响,SDK-go添加新的方法直接这种签名、构建和发送交易即可。
【系统信息】
拟支持的系统:要求Linux-x86、Linux-arm-86必须支持,其他信创、龙芯、麒麟、统信尽量支持
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information