## 1 工具需要做的配置及使用
工具以python脚本形式存在，在Ubuntu Linux上执行。  
预备条件：  
1.安装了go，版本go 1.12及以上，支持go mod。  
2.下载了plugin代码，并可以编译  
3.假定plugin代码路径为/home/user/code/go/plugin  

配置步骤：  
1.文件interface_check_tool.py中配置如下路径  
plugpath='/home/user/code/go/plugin/plugin'  
2.文件interface_check_tool.py中配置要核对的接口文档编号  
cids = ['93', '94', '100','99','98','97','96','102','105','104','108','110','115','118','134','135']  
这里的cid对应于url：https://chain.33.cn/document/93 中的最后一个编号，可以从https://chain.33.cn/网站上查看具体的接口文档的url。  
![接口文档编号](./image/cid.png)  
将所有需要核对接口的url中cid信息都添加到cids列表变量中。  
3.执行脚本python interface_check_tool.py  
脚本将通过类似https://mapi.bityuan.com/v1/article/helps?cid=93的方式，访问配置的所有接口文档。  
脚本将根据文档中的接口名称及路径信息，通过go doc命令从本地编译环境中的chain33工程代码或者plugin工程代码中得到对应消息接口的代码定义。  
脚本将上述两者中的相同接口的字段及类型信息进行比较，并记录结果。  
4.最终的汇总结果输出在脚本interface_check_tool.py的所在目录下的result文件中。  
```json
{
	"result":[
	{
	"cid":93,
	"result":[
		{
			"seq":1,
			"method":"Chain33.CreateRawTransaction.CreateTx",
			"msg":"CreateTx",
			"isSame":true,
			"diff":""
		},
		{
			"seq":2,
			"method":"Chain33.SignRawTx.ReqSignRawTx",
			"msg":"ReqSignRawTx",
			"isSame":true,
			"diff":""
		},
		{
			"seq":3,
			"method":"Chain33.SendTransaction.RawParm",
			"msg":"RawParm",
			"isSame":true,
			"diff":""
		},
		{
			"seq":4,
			"method":"Chain33.CreateNoBalanceTransaction.NoBalanceTx",
			"msg":"NoBalanceTx",
			"isSame":true,
			"diff":""
		},
		{
			"seq":5,
			"method":"Chain33.CreateNoBlanaceTxs.NoBalanceTxs",
			"msg":"NoBalanceTxs",
			"isSame":true,
			"diff":""
		},
		{
			"seq":6,
			"method":"Chain33.ReWriteRawTx.ReWriteRawTx",
			"msg":"ReWriteRawTx",
			"isSame":true,
			"diff":""
		},
		{
			"seq":7,
			"method":"Chain33.QueryTransaction.QueryParm",
			"msg":"QueryParm",
			"isSame":true,
			"diff":""
		},
		{
			"seq":8,
			"method":"Chain33.GetTxByAddr.ReqAddr",
			"msg":"ReqAddr",
			"isSame":true,
			"diff":""
		},
		{
			"seq":9,
			"method":"Chain33.GetTxByHashes.ReqHashes",
			"msg":"ReqHashes",
			"isSame":true,
			"diff":""
		},
		{
			"seq":10,
			"method":"Chain33.GetHexTxByHash.QueryParm",
			"msg":"QueryParm",
			"isSame":true,
			"diff":""
		},
		{
			"seq":11,
			"method":"Chain33.GetAddrOverview.ReqAddr",
			"msg":"ReqAddr",
			"isSame":true,
			"diff":""
		},
		{
			"seq":12,
			"method":"Chain33.ConvertExectoAddr.ExecNameParm",
			"msg":"ExecNameParm",
			"isSame":true,
			"diff":""
		},
		{
			"seq":13,
			"method":"Chain33.CreateRawTxGroup.CreateTransactionGroup",
			"msg":"CreateTransactionGroup",
			"isSame":true,
			"diff":""
		},
		{
			"seq":14,
			"method":"Chain33.GetProperFee.ReqProperFee",
			"msg":"ReqProperFee",
			"isSame":true,
			"diff":""
		}]
	}	]
}
```
结果信息中，注明了某一个接口文档中所有接口用到的接口消息定义与代码中的对比结果，如果某个接口消息对比结果不一致（文档和代码存在差异），会有具体的差异信息说明，文档编写人员可以核对、修正文档描述，确保文档的准确性。  


## 2 工具工作原理及文档格式要求  
文档格式需要符合约定要求，工具才能识别并进行自动化对比。  
![交易接口的文档格式](./image/tx_doc.png)
以交易接口为例，格式特点如下：  
```json
**请求报文[rpc/types/CreateTx]：**
json
{
    "jsonrpc":"2.0",
    "id":int32,
    "method":"Chain33.CreateRawTransaction",
    "params":[
		{
			"to":"string",
			"amount":int64,
			"fee":int64,
			"note":"string",
			"isToken":bool,
			"isWithdraw":bool,
			"tokenSymbol":"string",
			"execName":"string"
		}
	]
}

**参数说明：**

|参数|类型|是否必填|说明|
|----|----|----|----|
|to|string|是|发送到地址|
|amount|int64|是|发送金额，注意基础货币单位为10^8|
|fee|int64|是|手续费，注意基础货币单位为10^8|
|note|string|否|备注|
|isToken|bool|否|是否是token类型的转账 （非token转账这个不用填 包括平行链的基础代币转账也不用填）|
|isWithdraw|bool|是|是否为取款交易|
|tokenSymbol|string|否|token 的 symbol （非token转账这个不用填）|
|execName|string|是|TransferToExec（转到合约） 或 Withdraw（从合约中提款），如果要构造平行链上的转账，此参数置空|
|execer|string|-|执行器名称，如果是构造平行链的基础代币，此处要填写user.p.xxx.coins
```

(1)“请求报文[rpc/types/CreateTx]”关键字说明了这是一个请求接口，消息参数参见 chain33/rpc/types包里的CreateTx定义。  
(2)"method":"Chain33.CreateRawTransaction" 说明了具体的接口名称CreateRawTransaction。  
(3)"参数说明"，是文档中描述的CreateTx消息的字段定义。工具根据“----|----|----”特征认为是文档对字段说明的开始，并扫描连续的多个字段名及类型，直到遇到空行结束。  

检测脚本要完成的事情就是：  
（1）在chain33代码路径中，通过go doc rpc/types CreateTx得到代码中的CreateTx的字段定义。  
（2）从文档中解析出CreateTx的字段说明。  
（3）将两者对比，确认是否一致，如果不一致，记录差异。  

程序的处理流程：  
![程序处理流程设计](./image/design.png)  

目前支持对请求消息的字段的对比。  
由于响应消息的字段描述问题还比较多，暂时没有开启对响应消息的字段对比处理，待文档整改好以后再开启。  

## 3 文档格式样例

1.接口消息为chain33中的rpc/types 或者 types 包中的消息  
```json
#### 1.1.2 交易签名 SignRawTx

**请求报文[types/ReqSignRawTx]：**
` ` `json
{
    "jsonrpc":"2.0",
    "id":int32,
    "method":"Chain33.SignRawTx",
    "params":[
		{
			"addr":"string",
			"privkey":"string",
			"txHex":"string",
			"expire":"string",
			"index":int32,
			"token":"string",
			"fee":int64,
			"newToAddr":"string"
		}
	]
}
` ` `

**参数说明：**

|参数|类型|是否必填|说明|
|----|----|----|----|
|addr|string|否|addr与privkey可以只输入其一，如果使用addr则依赖钱包中存储的私钥签名|
|privkey|string|否|addr与privkey可以只输入其一，如果使用privkey则直接签名|
|txHex|string|是|上一步生成的原始交易数据|
|expire|string|是|过期时间可输入如"300s"，"-1.5h"或者"2h45m"的字符串，有效时间单位为"ns", "us" (or "µs"), "ms", "s", "m", "h"|
|index|int32|否|若是签名交易组，则为要签名的交易序号，从1开始，小于等于0则为签名组内全部交易|
|token|string|否||
|fee|int64|是|费用|
|newToAddr|string|否||

**响应报文：**
` ` `json
{
    "id":int32,
    "result":{"string"},
    "error":null
}
` ` `

**参数说明：**

|参数|类型|说明|
|----|----|----|
|result|string|交易签名后的十六进制字符串|
```

从chain33的工程代码中，找到接口定义：  
func (c *Chain33) SignRawTx(in *types.ReqSignRawTx, result *interface{}) error   
其中，请求参数ReqSignRawTx定义在types包中，所以，如下关键字指明包路径：  
**请求报文[types/ReqSignRawTx]：**  
响应消息是简单的string类型，可以不用检测（不需要在关键字“响应报文”后增加类似“[types/ReqSignRawTx]”的信息）。  

2.接口消息为plugin中的具体的包中的消息
```json
### 1.3 生成撤销创建token 的交易（未签名） CreateRawTokenRevokeTx

**请求报文[dapp/token/types/TokenRevokeCreate]：**

` ` `json
{
    "jsonrpc":"2.0",
    "id":int32,
    "method":"token.CreateRawTokenRevokeTx",
    "params":[{"symbol":"string","owner":"string"}]
}
` ` `
**参数说明：**

|参数|类型|是否必填|说明|
|----|----|----|----|
|symbol|string|是|token标记符，最大长度是16个字符，且必须为大写字符|
|owner|string|是|token拥有者地址|

**响应报文：**

` ` `json
{
    "id":int32,
    "error":null,
    "result":"string"
}
` ` `
**参数说明：**

|参数|类型|说明|
|----|----|----|
|result|string|交易对象的十六进制字符串编码|

**示例：**
Request:
` ` `json
{
	"id":1,
	"method":"token.CreateRawTokenRevokeTx",
	"params":[{"symbol":"DEVECOIN","owner":"14Gkp5kUEMo7fXhHuDRnRN6vJxNpSdDw1T"}]
}
` ` `
Response:
` ` `json
{
	"id":1,
	"result":"0a14757365722e702e646576746573742e746f6b656e12323",
	"error":null
}
` ` `
```

从plugin的工程代码中，找到接口定义：  
func (c *Jrpc) CreateRawTokenRevokeTx(param *tokenty.TokenRevokeCreate, result *interface{}) error  
其中，请求参数TokenRevokeCreate定义在dapp/token/types包中，所以，如下关键字指明其包路径：  
**请求报文[dapp/token/types/TokenRevokeCreate]：**  

响应消息是简单的string类型，可以不用检测（不需要在关键字“响应报文”后增加类似“[dapp/token/types/TokenRevokeCreate]”的信息）。  

3.接口消息为chain33中某个dapp的具体的包中的消息，Chain33.CreateTransaction接口中承载具体执行器的action信息
```json
### 1.1 添加/删除一个token-finisher CreateTransaction
**请求报文[types/ModifyConfig]：**
` ` `json
{
	"jsonrpc":"2.0",
    "id":int32,
    "method":"Chain33.CreateTransaction",
    "params":[
		{
			"execer" : "manage",
			"actionName" : "Modify",
			"payload" : {
				"key": "token-finisher",
				"value": "string",
				"op":"string",
				"addr":"string"
			}
		}
	]
}
` ` `

**参数说明：**

Chain33.CreateTransaction结构按通用要求填写：
execer,执行器名称，这里固定为manage
actionName,操作名称，这里固定为Modify
payload携带的内容格式如下：

|参数|类型|说明|
|----|----|----|
|key|string|目前支持token-finisher，token-blacklist|
|value|string|对应地址，如: 1CbEVT9RnM5oZhWMj4fxUrJX94VtRotzvs|
|op|string|操作方法，add 添加，delete 删除|
|addr|string|可为空|

**响应报文：**
` ` `json
{
    "id":int32,
    "result":"string"
}
` ` `
**参数说明：**

|参数|类型|说明|
|----|----|----|
|result|string|交易的hex字节码|
```
从chain33的工程代码中，找到具体的ModifyConfig消息定义,  
请求参数ModifyConfig定义在types包中，所以，如下关键字指明其包路径：  
**请求报文[types/ModifyConfig]：**  
响应消息是简单的string类型，可以不用检测（不需要在关键字“响应报文”后增加类似“[types/ModifyConfig]”的信息）。  

4.接口消息为plugin中某个dapp的具体的包中的消息，Chain33.Query接口中承载具体执行器的funcName信息

```json
### 6.3 根据创建事件的用户地址和事件所处状态查询事件ID QueryEventIDsByAddrAndStatus

**请求报文[dapp/oracle/types/QueryEventID]**
` ` `json
{
	"id":int32,
	"method": "Chain33.Query",
	"params": [
		{
			"execer": "oracle",
			"funcName": "QueryEventIDsByAddrAndStatus",
			"payload": {
				"status": 2,
				"addr": "14KEKbYtKKQm4wMthSK9J4La4nAiidGozt",
				"type": "",
				"eventID": ""
			}
		}
	]
}
` ` `

**参数说明**

Chain33.Query结构按通用要求填写：
execer,执行器名称，这里固定为oracle
funcName,操作名称，这里固定为QueryEventIDsByAddrAndStatus
payload携带的内容格式如下：

|参数|类型|是否必填|说明|
|----|----|----|----|
|status|int32|是|事件所处状态值|
|addr|string|是|创建事件的地址|
|type|string|不填|事件的类型|
|eventID|string|视情况|事件ID，第一次查询为空，如果得到结果数量超过一页，将eventID设置为上次查询结果的最后一个id，从而查到下一页的数据|

**响应报文[dapp/oracle/types/ReplyEventIDs]**
` ` `json
{
	"id":int32,
	"result": {
		"eventID": [
			"0xdd6b4ebfb7560e803cc4500490d2b7e6818296eed19d938446f2230eaa04a5e1",
			"0x1aae4290800019f6ba97ac45acba334aa919faff1a37c4adffce5eb12c6d3a06"
		]
	},
	"error": null
}
` ` `

**参数说明**

|参数|类型|说明|
|----|----|----|
|eventID|[]string|符合条件的事件ID数组|
```
从plugin的工程代码中，找到具体dapp/oracle中的QueryEventID消息定义,  
请求参数QueryEventID定义在dapp/oracle/types/包中，所以，如下关键字指明其包路径：  
**请求报文[dapp/oracle/types/QueryEventID]**  
响应消息是ReplyEventIDs结构类型，该类型定义在dapp/oracle/types/包中，所以，如下关键字知名其包路径：  
**响应报文[dapp/oracle/types/ReplyEventIDs]**  

## 4 重点强调

1.目前各个接口的请求消息已经做过统一修订，修改了很多字段缺失、类型描述错误、字段名称错误等问题，后续有新的接口或者接口变化时，请遵循上述要求，确保文档格式符合要求，这样检测工具可以正常工作。  
```json
**请求报文[types/ModifyConfig]：**               //严格按这个格式，[]中的内容按消息的实际所在包名及消息名进行拼接填写。
` ` `json
{
    ......
    "method":"Chain33.CreateTransaction",           //这个信息需要有，具体调用的是哪一个接口。
    ......
}
` ` `

**参数说明：**                                   //严格按照这个格式，检测工具据此判断文档进入参数说明段落，从后面会搜索具体参数说明。

|参数|类型|说明|                                  //表头严格按照这个格式。
|----|----|----|                                  //表头严格按照这个格式。检测工具据此认为下一行就是具体字段
|key|string|目前支持token-finisher，token-blacklist|   //具体字段的说明，如果字段为空，则没有这一行
......
                                                  //字段说明完毕，空一个空行，说明字段结束，检测工具据此判断停止解析字段。
```

对于接口由Chain33.CreateTransaction承载时，参见 3.3中的说明。  
对于接口由Chain33.Query承载时，参见3.4中的说明。  

2.对于各个接口的响应消息，还存在较多的问题，需要每个负责人按要求整改响应消息的描述，使之格式一致，符合统一标准，使检测工具可以检测响应消息。  
```json
**响应报文[dapp/oracle/types/ReplyEventIDs]**     //严格按照这个格式，[]中的内容按消息的实际所在包名及消息名进行拼接填写。 如果result为string类型，则不携带[]部分，不做检测。
` ` `json
{
	"id":int32,
	"result": {
		"eventID": [
			"0xdd6b4ebfb7560e803cc4500490d2b7e6818296eed19d938446f2230eaa04a5e1",
			"0x1aae4290800019f6ba97ac45acba334aa919faff1a37c4adffce5eb12c6d3a06"
		]
	},
	"error": null
}
` ` `

**参数说明**                                       //严格按照这个格式，检测工具据此判断文档进入参数说明段落，从后面会搜索具体参数说明。

|参数|类型|说明|                                    //表头严格按照这个格式。
|----|----|----|                                    //表头严格按照这个格式。检测工具据此认为下一行就是具体字段
|eventID|[]string|符合条件的事件ID数组|             //具体字段的说明，如果字段为空，则没有这一行
...... 
                                                    //字段说明完毕，空一个空行，说明字段结束，检测工具据此判断停止解析字段。
```
