动手编 SDK | BSN 联盟链开发(二)

人生,总避免不了开发几个 SDK (•̀ω•́ )

> **系列索引:** [上链与背后的流程 | 联盟链开发(一)](https://learnblockchain.cn/article/358) [动手编 SDK | 联盟链开发(二)](https://learnblockchain.cn/article/359) [SDK 1.0 版本的打造 | 联盟链开发(三)](https://learnblockchain.cn/article/380) [将 SDK 发布到 PIP | 联盟链开发(四)](https://learnblockchain.cn/article/385) [什么样的数据应该上链?| 联盟链开发 (五)](https://learnblockchain.cn/article/557) [BSN 相关问与答 | 联盟链开发(六)](https://learnblockchain.cn/article/683) [链上简历应用 — 设计 | 联盟链开发(七)](https://learnblockchain.cn/article/684) [FISCO BCOS 介绍 | 联盟链开发(八)](https://learnblockchain.cn/article/692) [WeIdentity 极速体验 | 联盟链开发(九)](https://learnblockchain.cn/article/1094) [给Remix升个级 | 联盟链开发(十)](https://learnblockchain.cn/article/984) [伪代码简述 ECDSA 签名过程 | 联盟链开发(十一)](https://learnblockchain.cn/article/1038) [WeIdentity 的多签及限量凭证的实现 | 联盟链开发(十二)](https://learnblockchain.cn/article/1285) --- 上回我们使用 BSN 官方提供的示例体验了一下上链操作及简要阐述了一下背后的原理。但是,由于仅为「示例」,所以代码耦合程度较高,要想「为我所用」还需要整理为 SDK。 自然可以将整理出来的代码直接放出来,但是记录与梳理「造轮子」的过程,对于自己的未来工作以及其它人未来的工作都会有帮助,因此我在这里会用几篇文章讲述如何一步步开发 SDK。 ## 1 一个趁手的 SDK 需要哪些要素 SDK 相当于现实生活中的一件工具。 那么,在生活中我们要发明一件让自己和别人使用的工具,需要哪些要素? - **文档友善** —— 需要友善的说明书告诉我怎么使用这个工具 - **可配置性好** —— 该能配置的地方需要配置不要写死 - **返回结果合理** —— 用工具加工出的产品需要在我们的预料之内 ## 2 以 get_data 为例进行函数改造 ### 2.1 解析源码 在官方提供的示例中,找到 views.py > get_data 函数,这个函数起到的作用是从链上查询信息: ```python def get_data(request): logger.info('\n #######进入get_data方法########') if request.method == 'POST': userCode = "reddate" appCode = "CL1851016378620191011150518" chainCode = "cc_base" funcName = "get" baseKey = request.POST.get('baseKey') if len(baseKey.strip()) == 0: return render(request, 'get.html', {'hint': '唯一标识不能为空!'}) logger.info('输入的baseKey:%s', baseKey) str = userCode + appCode + chainCode + funcName + baseKey logger.info('拼接待签名的字符串:%s', str) # 对字符串 A使用户证书的私钥进行 SHA256WITHECDSA签名 mac = myecdsa256.ecdsa_sign(str, './certificate/private_key.pem').decode() logger.info('base64格式mac值:%s', mac) url = 'https://quanzhounode.bsngate.com:17602/api/node/reqChainCode' headers = {'content-type': 'application/json'} datas = {"header": {"userCode": userCode, "appCode": appCode, "tId": "dc1d6010b7ff421dae0146f193dded09"}, "body": {"chainCode": chainCode, "funcName": funcName, "args": [baseKey]}, "mac": mac} logger.info('get_data请求传参:%s', datas) try: r = requests.post(url, headers=headers, json=datas, verify='./certificate/bsn_https.pem') if r.status_code == 200: result = r.json() …… ``` 我们可以看到,这个函数传入了一个 request 参数,然后拿了request 中的 baseKey 这个键的值,再通过一系列写死的参数加上 baseKey,去访问一个 url 最终得到结果。 ### 2.2 函数改造 如果要设计成 SDK 中的函数,那么要做如下改造: - 传入的不应该是request 类型的参数,而应该是一些更基本的值,例如字符串形式的 baseKey。 - 函数应当进一步拆分,拆分为`get_data` 和 `__do_get_data `两个函数,使代码结构更清晰。 因此,首先设计一个函数`get_data(baseKey, url)`: ```python def get_data(baseKey, url): userCode = "reddate" appCode = "CL1851016378620191011150518" chainCode = "cc_base" funcName = "get" if len(baseKey.strip()) == 0: logger.error("baseKey can not be null") else: return __do_get_data(userCode, appCode, chainCode, funcName, baseKey, url) ``` 然后设计一个`__do_get_data(...)`: ```python def __do_get_data(userCode, appCode, chainCode, funcName, baseKey, url): payload = userCode + appCode + chainCode + funcName + baseKey logger.info('拼接待签名的字符串:%s', payload) mac = myecdsa256.ecdsa_sign(payload, './certificate/private_key.pem').decode() logger.info('base64格式mac值:%s', mac) data = {"header": {"userCode": userCode, "appCode": appCode, "tId": "dc1d6010b7ff421dae0146f193dded09"}, "body": {"chainCode": chainCode, "funcName": funcName, "args": [baseKey]}, "mac": mac} headers = {'content-type': 'application/json'} res = requests.post(url, headers=headers, json=data, verify='./certificate/bsn_https.pem') return res.json() ``` 那么,我们就获得了这样一个函数`get_data`,传入 `baseKey `和 节点地址 `url`,最终得到从链上拿下来的 json。 ## 3 类的设计 Python 是一门基于面向对象思想的语言,所以函数最好还是放到一个类里面。因此设计一个叫做 Operator 的类。 ```python import requests from common.loggers import logger from common import myecdsa256 class Operator: def get_data(self, baseKey, url): …… else: return self.__do_get_data(userCode, appCode, chainCode, funcName, baseKey, url) def __do_get_data(self, userCode, appCode, chainCode, funcName, baseKey, url): …… ``` ## 4 init 文件 为了方便调取,在 sdk 文件夹下创建了`__init__.py`文件: ```python from sdk.operator import Operator ``` 目录结构目前如下: [![](https://learnblockchain.cn/image/show/attachments-2020-01-oibwzuhN5e1fdf6f0310d.png)](https://learnblockchain.cn/image/show/attachments-2020-01-oibwzuhN5e1fdf6f0310d.png) ## 5 测试 现在来看一下效果如何: [![](https://learnblockchain.cn/image/show/attachments-2020-01-k7B9OXR85e1fdf7bdb924.png)](https://learnblockchain.cn/image/show/attachments-2020-01-k7B9OXR85e1fdf7bdb924.png) Okay,如期拿到了链上结果。 [![](https://learnblockchain.cn/image/show/attachments-2020-01-dLd6W0uv5e1fdfb682a76.jpeg)](https://learnblockchain.cn/image/show/attachments-2020-01-dLd6W0uv5e1fdfb682a76.jpeg)

系列索引: 上链与背后的流程 | 联盟链开发(一) 动手编 SDK | 联盟链开发(二) SDK 1.0 版本的打造 | 联盟链开发(三) 将 SDK 发布到 PIP | 联盟链开发(四) 什么样的数据应该上链?| 联盟链开发 (五) BSN 相关问与答 | 联盟链开发(六) 链上简历应用 — 设计 | 联盟链开发(七) FISCO BCOS 介绍 | 联盟链开发(八) WeIdentity 极速体验 | 联盟链开发(九) 给Remix升个级 | 联盟链开发(十) 伪代码简述 ECDSA 签名过程 | 联盟链开发(十一) WeIdentity 的多签及限量凭证的实现 | 联盟链开发(十二)

上回我们使用 BSN 官方提供的示例体验了一下上链操作及简要阐述了一下背后的原理。但是,由于仅为「示例」,所以代码耦合程度较高,要想「为我所用」还需要整理为 SDK。

自然可以将整理出来的代码直接放出来,但是记录与梳理「造轮子」的过程,对于自己的未来工作以及其它人未来的工作都会有帮助,因此我在这里会用几篇文章讲述如何一步步开发 SDK。

1 一个趁手的 SDK 需要哪些要素

SDK 相当于现实生活中的一件工具。

那么,在生活中我们要发明一件让自己和别人使用的工具,需要哪些要素?

  • 文档友善 —— 需要友善的说明书告诉我怎么使用这个工具
  • 可配置性好 —— 该能配置的地方需要配置不要写死
  • 返回结果合理 —— 用工具加工出的产品需要在我们的预料之内

2 以 get_data 为例进行函数改造

2.1 解析源码

在官方提供的示例中,找到 views.py > get_data 函数,这个函数起到的作用是从链上查询信息:

def get_data(request):

    logger.info('\n #######进入get_data方法########')

    if request.method == 'POST':
        userCode = "reddate"
        appCode = "CL1851016378620191011150518"
        chainCode = "cc_base"
        funcName = "get"
    baseKey = request.POST.get('baseKey')
    if len(baseKey.strip()) == 0:
            return render(request, 'get.html', {'hint': '唯一标识不能为空!'})

        logger.info('输入的baseKey:%s', baseKey)

        str = userCode + appCode + chainCode + funcName + baseKey
        logger.info('拼接待签名的字符串:%s', str)

        # 对字符串 A使用户证书的私钥进行 SHA256WITHECDSA签名
        mac = myecdsa256.ecdsa_sign(str, './certificate/private_key.pem').decode()
        logger.info('base64格式mac值:%s', mac)

        url = 'https://quanzhounode.bsngate.com:17602/api/node/reqChainCode'
        headers = {'content-type': 'application/json'}

        datas = {"header": {"userCode": userCode, "appCode": appCode, "tId": "dc1d6010b7ff421dae0146f193dded09"},
                 "body": {"chainCode": chainCode, "funcName": funcName, "args": [baseKey]},
                 "mac": mac}

        logger.info('get_data请求传参:%s', datas)

        try:
            r = requests.post(url, headers=headers, json=datas, verify='./certificate/bsn_https.pem')
            if r.status_code == 200:
                result = r.json()
        ……

我们可以看到,这个函数传入了一个 request 参数,然后拿了request 中的 baseKey 这个键的值,再通过一系列写死的参数加上 baseKey,去访问一个 url 最终得到结果。

2.2 函数改造

如果要设计成 SDK 中的函数,那么要做如下改造:

  • 传入的不应该是request 类型的参数,而应该是一些更基本的值,例如字符串形式的 baseKey。
  • 函数应当进一步拆分,拆分为get_data__do_get_data两个函数,使代码结构更清晰。

因此,首先设计一个函数get_data(baseKey, url)

def get_data(baseKey, url):
    userCode = "reddate"
    appCode = "CL1851016378620191011150518"
    chainCode = "cc_base"
    funcName = "get"
        if len(baseKey.strip()) == 0:
            logger.error("baseKey can not be null")
        else:
            return __do_get_data(userCode, appCode, chainCode, funcName, baseKey, url)

然后设计一个__do_get_data(...)

def __do_get_data(userCode, appCode, chainCode, funcName, baseKey, url):
    payload = userCode + appCode + chainCode + funcName + baseKey
    logger.info('拼接待签名的字符串:%s', payload)

    mac = myecdsa256.ecdsa_sign(payload, './certificate/private_key.pem').decode()
    logger.info('base64格式mac值:%s', mac)

    data = {"header": {"userCode": userCode, "appCode": appCode, "tId": "dc1d6010b7ff421dae0146f193dded09"},
                "body": {"chainCode": chainCode, "funcName": funcName, "args": [baseKey]},
                "mac": mac}
    headers = {'content-type': 'application/json'}
    res = requests.post(url, headers=headers, json=data, verify='./certificate/bsn_https.pem')
    return res.json()

那么,我们就获得了这样一个函数get_data,传入 baseKey和 节点地址 url,最终得到从链上拿下来的 json。

3 类的设计

Python 是一门基于面向对象思想的语言,所以函数最好还是放到一个类里面。因此设计一个叫做 Operator 的类。

import requests
from common.loggers import logger
from common import myecdsa256

class Operator:
    def get_data(self, baseKey, url):
    ……
    else:
        return self.__do_get_data(userCode, appCode, chainCode, funcName, baseKey, url)

    def __do_get_data(self, userCode, appCode, chainCode, funcName, baseKey, url):
    ……

4 init 文件

为了方便调取,在 sdk 文件夹下创建了__init__.py文件:

from sdk.operator import Operator

目录结构目前如下:

5 测试

现在来看一下效果如何:

Okay,如期拿到了链上结果。

区块链技术网。

  • 发表于 2020-01-16 11:51
  • 阅读 ( 1465 )
  • 学分 ( 107 )
  • 分类:BSN

评论