16. 预言机使用文档

16.1. 安装和配置

16.1.1. 服务依赖

16.1.1.1. 操作系统

预言机合约运行在Docker VM中,当前仅支持部署在Linux系统

16.1.1.2. docker方式

  • git

  • docker 18+

  • docker-compose 1.29.2

16.1.1.3. 源码方式

  • Mysql 5.7+

  • golang 1.16+

16.1.2. 源码下载

  • 下载代码

$ git clone -b v1.0.0_alpha https://git.chainmaker.org.cn/chainmaker/chainmaker-oracle

16.1.3. 服务配置

16.1.3.1. 整体配置目录结构如下:

├── config_files # 配置文件
│   ├── crypto-config # 使用chainmaker-cryptogen 工具生成的证书文件
│   ├── sdk-config # 链sdk配置
│   │   ├── sdk_config_chain1.yml # 链1的sdk
│   │   └── sdk_config_chain2.yml # 链2的sdk
│   └── smart_oracle.yml # 预言机服务的配置文件

16.1.3.2. 预言机配置结构(smart_oracle.yml):

用户需要关注如下配置,详情参考完整的配置文件

# 链证书配置相关
sdks: 

# 预言机数据库配置
oracle_mysql:

# 预言机提供的mysql数据源
datasource_mysql:
  - datasource: local
    user_name: test
    password: 123456@
    db_address: oracle_db:3306 
    database: chain_oracle
    max_open: 2
    max_idle: 1
    max_idle_seconds: 180
    max_life_seconds: 1800

16.1.4. 快速启动一条docker-vm引擎的链(已有链则跳过)

$ git clone https://git.chainmaker.org.cn/chainmaker/chainmaker-go.git
$ git clone -b v2.2.0 https://git.chainmaker.org.cn/chainmaker/chainmaker-cryptogen.git
$ cd chainmaker-cryptogen && make
$ cd ../chainmaker-go/tools/ && ln -s ../../chainmaker-cryptogen/ .
# 修改下链的grpc配置中消息大小,链的chainmaker.yml文件
$ cd ../config/config_tpl
$ sed -i 's/max_send_msg_size: 10/max_send_msg_size: 200/g' chainmaker.tpl
$ sed -i 's/max_recv_msg_size: 10/max_recv_msg_size: 200/g' chainmaker.tpl
$ cd ../../scripts
$ ./prepare.sh 4 2
	1
	INFO
	YES
$ ./build_release.sh
$ ./cluster_quick_start.sh normal

16.1.5. 运行服务方式一:使用docker-compose启动

  • 若需自定义则修改下方配置信息

    • 修改文件chainmaker-oralce/config_files/smart_oracle.yml

    • 修改文件chainmaker-oralce/config_files/sdk-config/sdk_config_chain1.yml

    • 添加证书chainmaker-oralce/config_files/crypto-config

  • 执行docker-compose up即可启动服务

# 设置证书
$ cd chainmaker-oracle 
$ rm -rf config_files/crypto-config/ && cp -rf ../chainmaker-go/build/crypto-config/ config_files/
# 修改链节点地址为局域网IP
$ vim config_files/sdk-config/sdk_config_chain1.yml
$ vim config_files/sdk-config/sdk_config_chain2.yml

# 启动服务
$ docker-compose up 

# 看到如下日志表示成功启动:
  http service start , port :10123

# 查看服务健康状态
$ curl -X GET localhost:10123/v1/health
$ curl -X GET localhost:10124/v1/health
# 查看日志  
$ docker logs -f --tail 100 chainmaker-oracle-1
$ docker logs -f --tail 100 chainmaker-oracle-2
  • 停止服务

$ docker-compse down 
# 删除mysql中的数据
$ rm -rf ./mysql-data-volumes/data/

16.1.6. 运行服务方式二:使用源码启动

  • 编译:

$ cd chainmaker-oracle && go build -o chainmaker-oracle
  • 初始化数据库: 数据库上执行代码的scripts/init.sql脚本文件

$ mysql -uroot -p < scripts/init.sql 
  • 修改配置信息

    • 修改文件chainmaker-oralce/config_files/smart_oracle.yml

    • 修改文件chainmaker-oralce/config_files/sdk-config/sdk_config_chain1.yml

    • 添加证书chainmaker-oralce/config_files/crypto-config

  • 启动服务

# 直接启动
$ ./chainmaker-oracle 
# 或指定配置文件启动
$ ./chainmaker-oracle -i config_files/smart_oracle.yml 

# 看到如下日志表示成功启动:
  http service start , port :10123
  
# 查看服务健康状态
$ curl -X GET localhost:10123/v1/health

# 查看日志  
$ tail -100 log/system.log 

# 停止服务
$ kill ${pid}

16.1.7. 预言机合约安装

  • 预言机合约的安装是通过http接口的形式给出,可以直接调用接口

# admin_token 为配置中的admin_token
# address 为预言机监听,接收http请求地址
# chain_alias 为配置文件smart_oracle.yml中的chain_alias
# oracle-contract 为预言机合约二进制压缩文件(默认为.7z)
$ curl -H "admin_auth_token: ${admin_token}" -X POST ${address}/v1/install_contract -F "chain_alias=${chain_alias}" -F "contract_version=1" -F "runtime=DOCKER_GO" -F "contract_file=@${oracle-contract}"
# 示例:
$ cd chainmaker-oracle 
$ 7z a oracle_contract_v1.7z standard_oracle_contract/oracle_contract_file/oracle_contract_v1
# 给chain1安装oracle合约
$ curl -H 'admin_auth_token: si!*dfji@12mnku' -X POST localhost:10123/v1/install_contract -F "chain_alias=chain1_alias" -F "contract_version=1" -F "runtime=DOCKER_GO" -F "contract_file=@./oracle_contract_v1.7z"
# 给chain2安装oracle合约
$ curl -H 'admin_auth_token: si!*dfji@12mnku' -X POST localhost:10123/v1/install_contract -F "chain_alias=chain2_alias" -F "contract_version=1" -F "runtime=DOCKER_GO" -F "contract_file=@./oracle_contract_v1.7z"
  • 为了方便使用,提供了install-upgrade-oracle-contract.sh安装脚本,可以按照命令提示也可以安装

$ ./install-upgrade-oracle-contract.sh 

16.1.8. 使用用户合约取数据(以cmc工具为例)

16.1.8.1. 编译CMC

$ git clone https://git.chainmaker.org.cn/chainmaker/chainmaker-go.git
$ cd chainmaker-go/
$ make cmc
$ cp bin/cmc ../chainmaker-oracle/ && cd ../chainmaker-oracle/

16.1.8.2. 使用cmc工具安装示例用户合约文件

# 链1上安装用户合约
./cmc client contract user create --contract-name=use_demo --runtime-type=DOCKER_GO --byte-code-path=./standard_oracle_contract/use_demo.7z --version=1 --sdk-conf-path=./config_files/sdk-config/sdk_config_chain1.yml  --admin-key-file-paths=./config_files/crypto-config/wx-org1.chainmaker.org/user/admin1/admin1.tls.key,./config_files/crypto-config/wx-org2.chainmaker.org/user/admin1/admin1.tls.key,./config_files/crypto-config/wx-org3.chainmaker.org/user/admin1/admin1.tls.key,./config_files/crypto-config/wx-org4.chainmaker.org/user/admin1/admin1.tls.key --admin-crt-file-paths=./config_files/crypto-config/wx-org1.chainmaker.org/user/admin1/admin1.tls.crt,./config_files/crypto-config/wx-org2.chainmaker.org/user/admin1/admin1.tls.crt,./config_files/crypto-config/wx-org3.chainmaker.org/user/admin1/admin1.tls.crt,./config_files/crypto-config/wx-org4.chainmaker.org/user/admin1/admin1.tls.crt --sync-result=true --params="{\"chainId\":\"chain1\",\"version\":\"1\"}" --chain-id=chain1
# 链2上安装用户合约
./cmc client contract user create --contract-name=use_demo --runtime-type=DOCKER_GO --byte-code-path=./standard_oracle_contract/use_demo.7z --version=1 --sdk-conf-path=./config_files/sdk-config/sdk_config_chain2.yml  --admin-key-file-paths=./config_files/crypto-config/wx-org1.chainmaker.org/user/admin1/admin1.tls.key,./config_files/crypto-config/wx-org2.chainmaker.org/user/admin1/admin1.tls.key,./config_files/crypto-config/wx-org3.chainmaker.org/user/admin1/admin1.tls.key,./config_files/crypto-config/wx-org4.chainmaker.org/user/admin1/admin1.tls.key --admin-crt-file-paths=./config_files/crypto-config/wx-org1.chainmaker.org/user/admin1/admin1.tls.crt,./config_files/crypto-config/wx-org2.chainmaker.org/user/admin1/admin1.tls.crt,./config_files/crypto-config/wx-org3.chainmaker.org/user/admin1/admin1.tls.crt,./config_files/crypto-config/wx-org4.chainmaker.org/user/admin1/admin1.tls.crt --sync-result=true --params="{\"chainId\":\"chain2\",\"version\":\"1\"}" --chain-id=chain2

16.1.8.3. 使用管理员身份(也即为安装预言机合约相同的签名)注册查询topic,使用普通用户身份根据topic查询

# 注册topic[event_state_query],查询数据源local的表event_state
./cmc client contract user invoke --contract-name=oracle_contract_v1 --method=invoke_contract --sdk-conf-path=./config_files/sdk-config/sdk_config_chain1.yml --params="{\"method\":\"registerQueryTopic\",\"topic\":\"event_state_query\",\"method_type\":\"queryMysql\",\"query_body\": \"{\\\"sql_sentence\\\":\\\"select * from event_state \\\",\\\"data_source\\\":\\\"local\\\",\\\"sql_type\\\":\\\"select\\\"}\" }" --sync-result=true  --chain-id=chain1   

# 使用topic[event_state_query]查询
./cmc client contract user invoke --contract-name=use_demo --method=invoke_contract --sdk-conf-path=./config_files/sdk-config/sdk_config_chain1.yml  --params="{\"method\":\"query_topic\",\"topic\":\"event_state_query\"}" --sync-result=true  --chain-id=chain1 

16.1.8.4. 取json类型数据

./cmc client contract user invoke --contract-name=use_demo --method=invoke_contract --sdk-conf-path=./config_files/sdk-config/sdk_config_chain1.yml --params="{\"method\":\"query_http\",\"url\":\"https://v0.yiketianqi.com/api?unescape=1&version=v61&appid=88684831&appsecret=OsSX6jwW\",\"fetch_data_type\":\"json\",\"fetch_data_formular\":\"\/aqi\/no2_desc\",\"max_retry\":\"3\",\"connection_timeout\":\"20\",\"max_fetch_timeout\":\"20\"}" --sync-result=true  --chain-id=chain1

结果:INVOKE contract resp, [code:0]/[msg:]/[contractResult:result:”status:200 payload:”{\”result\”:null,\”code\”:\”202\”,\”message\”:\”success\”,\”response_hash\”:\”b1ee8be6f122612f1bfeac335ba1f2f3\”}” “ message:”Success” gas_used:18510 contract_event:<topic:”oracle_query” tx_id:”16fc7e533eabd3d7ca52fdfc0721826594cce7e90d084fc890053e15aa8b479f” contract_name:”oracle_contract_v1” contract_version:”1” event_data:”1” event_data:”chain1_alias” event_data:”b1ee8be6f122612f1bfeac335ba1f2f3” event_data:”16fc7e533eabd3d7ca52fdfc0721826594cce7e90d084fc890053e15aa8b479f” event_data:”7” event_data:”{”method”:”GET”,”url”:”https://v0.yiketianqi.com/api?unescape=1\u0026version=v61\u0026appid=88684831\u0026appsecret=OsSX6jwW”,”http_header”:null,”http_body”:null,”connection_timeout”:30,”max_fetch_timeout”:30,”fetch_data_type”:”json”,”fetch_data_formula”:”/aqi/no2_desc”}” > ]/[txId:16fc7e533eabd3d7ca52fdfc0721826594cce7e90d084fc890053e15aa8b479f]

16.1.8.5. 取html数据

./cmc client contract user invoke --contract-name=use_demo --method=invoke_contract --sdk-conf-path=./config_files/sdk-config/sdk_config_chain1.yml --params="{\"method\":\"query_http\",\"url\":\"https://www.baidu.com\",\"http_body\":null,\"connection_timeout\":\"5\",\"max_retry\":\"3\",\"max_fetch_timeout\":\"10\",\"fetch_data_type\":\"html\",\"fetch_data_formular\":\"\/\/title\"}" --sync-result=true  --chain-id=chain1

结果:INVOKE contract resp, [code:0]/[msg:]/[contractResult:result:”status:200 payload:”{\”result\”:null,\”code\”:\”202\”,\”message\”:\”success\”,\”response_hash\”:\”30a004cdc8b2f4e80876dbed381fd3e7\”}” “ message:”Success” gas_used:19115 contract_event:<topic:”oracle_query” tx_id:”16fc7e5c927ec90fca52fdfc07218265daa02eb765794c77bb2deceebd4a4b05” contract_name:”oracle_contract_v1” contract_version:”1” event_data:”1” event_data:”chain1_alias” event_data:”30a004cdc8b2f4e80876dbed381fd3e7” event_data:”16fc7e5c927ec90fca52fdfc07218265daa02eb765794c77bb2deceebd4a4b05” event_data:”9” event_data:”{”method”:”GET”,”url”:”https://www.baidu.com”,”http_header”:{”User-Agent”:”Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3776.0 Safari/537.36”},”http_body”:null,”connection_timeout”:30,”max_fetch_timeout”:30,”fetch_data_type”:”html”,”fetch_data_formula”:”//title”}” > ]/[txId:16fc7e5c927ec90fca52fdfc07218265daa02eb765794c77bb2deceebd4a4b05]

16.1.8.6. 取xml数据

./cmc client contract user invoke --contract-name=use_demo --method=invoke_contract --sdk-conf-path=./config_files/sdk-config/sdk_config_chain1.yml --params="{\"method\":\"query_http\",\"url\":\"https://www.w3school.com.cn/example/xmle/note.xml\",\"http_body\":null,\"connection_timeout\":\"5\",\"max_retry\":\"3\",\"max_fetch_timeout\":\"10\",\"fetch_data_type\":\"xml\",\"fetch_data_formular\":\"\/\/note\"}" --sync-result=true  --chain-id=chain1

结果: INVOKE contract resp, [code:0]/[msg:]/[contractResult:result:”status:200 payload:”{\”result\”:null,\”code\”:\”202\”,\”message\”:\”success\”,\”response_hash\”:\”4ca4a8cb3f2907eb5dac3f6f673fec0e\”}” “ message:”Success” gas_used:17611 contract_event:<topic:”oracle_query” tx_id:”16fc7e62554560a5ca52fdfc072182656b74385f041e4da8aadf68e78727e06a” contract_name:”oracle_contract_v1” contract_version:”1” event_data:”1” event_data:”chain1_alias” event_data:”4ca4a8cb3f2907eb5dac3f6f673fec0e” event_data:”16fc7e62554560a5ca52fdfc072182656b74385f041e4da8aadf68e78727e06a” event_data:”11” event_data:”{”method”:”GET”,”url”:”https://www.w3school.com.cn/example/xmle/note.xml”,”http_header”:null,”http_body”:null,”connection_timeout”:30,”max_fetch_timeout”:30,”fetch_data_type”:”xml”,”fetch_data_formula”:”//note”}” > ]/[txId:16fc7e62554560a5ca52fdfc072182656b74385f041e4da8aadf68e78727e06a]

16.1.8.7. 取VRF数据

./cmc client contract user invoke --contract-name=use_demo --method=invoke_contract --sdk-conf-path=./config_files/sdk-config/sdk_config_chain1.yml --params="{\"method\":\"get_vrf\",\"alpha\":\"f12vvio\"}" --sync-result=true  --chain-id=chain1

结果:INVOKE contract resp, [code:0]/[msg:]/[contractResult:result:”status:200 payload:”{\”result\”:null,\”code\”:\”202\”,\”message\”:\”success\”,\”response_hash\”:\”dce88bc54c62200bfbdc24f755535d54\”}” “ message:”Success” gas_used:14431 contract_event:<topic:”oracle_query” tx_id:”16fc7e662a606536ca52fdfc07218265bf42b16723af4d7db03dec55a11dea46” contract_name:”oracle_contract_v1” contract_version:”1” event_data:”3” event_data:”chain1_alias” event_data:”dce88bc54c62200bfbdc24f755535d54” event_data:”16fc7e662a606536ca52fdfc07218265bf42b16723af4d7db03dec55a11dea46” event_data:”13” event_data:”f12vvio” > ]/[txId:16fc7e662a606536ca52fdfc07218265bf42b16723af4d7db03dec55a11dea46]

16.1.8.8. 取mysql数据

./cmc client contract user invoke --contract-name=use_demo --method=invoke_contract --sdk-conf-path=./config_files/sdk-config/sdk_config_chain1.yml --params="{\"method\":\"query_mysql\",\"sql_type\":\"select\",\"page_num\":\"1\",\"page_size\":\"10\",\"sql_sentence\":\"select * from event_result \",\"data_source\":\"mysql\"}" --sync-result=true  --chain-id=chain1

INVOKE contract resp, [code:0]/[msg:]/[contractResult:result:”{”result”:null,”code”:”202”,”message”:”success”,”response_hash”:”3896ca045ed94ff9c3d5add90f206b0b”}” message:”Success” gas_used:15706 contract_event:<topic:”oracle_query” tx_id:”16fc7e6902f78cafca52fdfc07218265dc4d346d146b4b08a69f088f842d5585” contract_name:”oracle_contract_v1” contract_version:”1” event_data:”2” event_data:”chain1_alias” event_data:”3896ca045ed94ff9c3d5add90f206b0b” event_data:”16fc7e6902f78cafca52fdfc07218265dc4d346d146b4b08a69f088f842d5585” event_data:”15” event_data:”{”sql_type”:”select”,”sql_sentence”:”select * from event_result “,”data_source”:”mysql”}” > ]/[txId:16fc7e6902f78cafca52fdfc07218265dc4d346d146b4b08a69f088f842d5585]

16.1.8.9. 使用hash查询数据(注意hash为预言机合约返回给用户合约的hash值)

从上方结果中任意取一个response_hash填充到query_hash中。

./cmc client contract user invoke --contract-name=use_demo --method=invoke_contract --sdk-conf-path=./config_files/sdk-config/sdk_config_chain1.yml --params="{\"method\":\"query_hash\",\"query_hash\":\"3896ca045ed94ff9c3d5add90f206b0b\"}" --sync-result=true  --chain-id=chain1

结果:INVOKE contract resp, [code:0]/[msg:]/[contractResult:result:”{”result”:null,”code”:”201”,”message”:”query success, get no result”,”response_hash”:””}” message:”Success” gas_used:10259 ]/[txId:16fc7e6e0343ce7fca52fdfc07218265e1778052f320478c939d3c86a8c57cad]

16.1.8.10. 跨链查询数据(注意hash为预言机合约返回给用户合约的hash值)

示例中从上方结果中任意取一个response_hash填充到query_hash中(此为用户合约行为,可自定义)。

./cmc client contract user invoke --contract-name=use_demo --method=invoke_contract --sdk-conf-path=./config_files/sdk-config/sdk_config_chain1.yml --params="{\"method\":\"query_hash\",\"query_hash\":\"3896ca045ed94ff9c3d5add90f206b0b\"}" --sync-result=true  --chain-id=chain1
# 注意query_hash 需要为查询返回的值
./cmc client contract user invoke --contract-name=use_demo --method=invoke_contract --sdk-conf-path=./config_files/sdk-config/sdk_config_chain1.yml --params="{\"method\":\"query_cross\",\"chain_alias\":\"chain1_alias\",\"contract_name\":\"use_demo\",\"method_name\":\"query_hash\", \"query_hash\":\"conf\"}" --sync-result=true  --chain-id=chain2

结果:INVOKE contract resp, [code:0]/[msg:]/[contractResult:result:”QueryCross ok” message:”Success” gas_used:16445 contract_event:<topic:”oracle_query” tx_id:”16fc7e8add879fd8ca52fdfc0721826578760f8515c049599c29e3cadc3a382e” contract_name:”oracle_contract_v1” contract_version:”1” event_data:”5” event_data:”chain2_alias” event_data:”55193d721576a16e21b2a110d7f11bd3” event_data:”16fc7e8add879fd8ca52fdfc0721826578760f8515c049599c29e3cadc3a382e” event_data:”6” event_data:”{”chain_alias”:”chain1_alias”,”contract_name”:”use_demo”,”method_name”:”query_hash”,”params”:[{”key”:”query_hash”,”value”:”Y29uZg==”}]}” > ]/[txId:16fc7e8add879fd8ca52fdfc0721826578760f8515c049599c29e3cadc3a382e]

16.2. 通过开发智能合约来操作预言机取数据

预言机通过在链上部署预言机智能合约oracle_contract,来给用户提供取数据功能。用户可以通过调用oracle_contract相应接口即可。由于取数据是一个不确定运行时长的过程,因此我们采用的是调用->事件->回调的方式。用户合约告知预言机需要取的数据类型,预言机取到数据通过用户合约注册的回调函数来告知用户合约

16.2.1. 用户合约的安装,安装时候需要查询一下预言机合约的公钥签名,以便后续校验数据

func (o *UseDemo) getOralceContractPk(stub shim.CMStubInterface) (string, error) {
	parameters := make(map[string][]byte)
	parameters["method"] = []byte("queryOracleContractPK")
	resp := stub.CallContract("oracle_contract_v1", "1", parameters)
	if resp.Status != 200 {
		stub.Log("getOralceContractPk error," + resp.String())
		return "", errors.New(resp.String())
	}
	var responeT OracleQueryResult
	uerr := json.Unmarshal(resp.Payload, &responeT)
	if uerr != nil {
		stub.Log("getOralceContractPk unmarshal error")
		return "", errors.New("getOralceContractPk unmarshal error" + uerr.Error())
	}
	return string(responeT.ResultB), nil
}

func (o *UseDemo) InitContract(stub shim.CMStubInterface) protogo.Response {
	// 首先获取一下预言机合约的公钥签名,缓存一下,后续做callback的时候可以校验
	oraclePk, oraclePkErr := o.getOralceContractPk(stub)
	if oraclePkErr != nil {
		return shim.Error(oraclePkErr.Error())
	}
	storeErr := stub.PutStateFromKey(gOracleContractPkStr, oraclePk)
	if storeErr != nil {
		stub.Log("InitContract store oracle_contract_pk error")
		return shim.Error(storeErr.Error())
	}

	version := string(stub.GetArgs()["version"])
	if strings.TrimSpace(version) == "" {
		version = "1"
	}
	err := stub.PutStateFromKey("version", version)
	if err != nil {
		stub.Log("InitContract fail to save version")
		return shim.Error("fail to save version")
	}
	stub.Log(fmt.Sprintf("InitContract version(%s) ", version))
	return shim.Success([]byte("Init Success"))
}

16.2.2. 取VRF功能开发

调用参数

	parameters := make(map[string][]byte)
	parameters["alpha"] = []byte(alpha) //用户输入信息,来计算随机数使用
	parameters["method"] = []byte("get_vrf") //调用预言机的vrf功能
	parameters["original_contract_name"] = []byte("use_demo") //用户自己合约的名称
	parameters["original_contract_version"] = []byte(version) //用户自己合约的版本

返回参数:

type OracleQueryResult struct {
	ResultB      []byte `json:"result"`        //查询结果的json序列化
	Code         string `json:"code"`          //错误码
	Message      string `json:"message"`       //错误信息
	ResponseHash string `json:"response_hash"` //返回唯一hash值
}

取数据回调:

	args["result"]              //调用结果数据
	args["response_hash"]  //调用的唯一索引值,与上文的返回参数唯一索引值一样
	args["code"]           //错误码
	args["message"]    //错误信息

其中result中即为所取到的VRF信息,其为如下结构体的json序类化字符串

type Random struct {
	RandData string  `json:"rand_data"` //32 byte的bigint
	Pi       []byte  `json:"pi"` //证据
	Ratio    float64 `json:"ratio"` //[0,1]之间的随机数
}

16.2.3. 取接口数据功能开发(xpath语法)

调用参数

type ModelHttp struct {
	Method     string            `json:"method"` //GET
	URL        string            `json:"url"`
	HttpHeader map[string]string `json:"http_header"`
	HttpBody   []byte            `json:"http_body"`
	ConnectionTimeout int `json:"connection_timeout"` //最大连接超时时长,默认30-60s,单位秒级别
	MaxFetchTimeout   int `json:"max_fetch_timeout"`  //最大读取超时时长,默认10s,可以选择为10-60s
	FetchDataType    string `json:"fetch_data_type"`    //json,xml,html
	FetchDataFormula string `json:"fetch_data_formula"` //默认为空,为空则不解析,原样返回。如果非空,则按照xpath来分隔取出来结构化的数据
}


	parameters["http_query"] = httpBs // ModelHttp 序列化后的字符串
	parameters["method"] = []byte("queryHttp") //调用预言机的接口数据功能
	parameters["original_contract_name"] = []byte("use_demo") //用户自己合约的名称
	parameters["original_contract_version"] = []byte(version) //用户自己合约的版本
	parameters["use_chaindata"] = []byte(useCache) //如果链上已经有相同的查询参数做的查询,是否使用链上已有数据返回
	parameters["is_persistence"] = []byte(isPersist) //查询结果是否保存到链上,便于后面可以根据查询参数做查询

16.2.4. 取mysql数据功能开发

调用参数:

type ModelSql struct {
	SqlType     string `json:"sql_type"` //select
	SqlSentence string `json:"sql_sentence"`// sql语句
	DataSource  string `json:"data_source"` //数据源
}
	parameters["mysql_query"] = sqlBs // ModelSql 序列化后的字符串
	parameters["method"] = []byte("queryMysql") //调用预言机的查询mysql数据功能
	parameters["original_contract_name"] = []byte("use_demo") //用户自己合约的名称
	parameters["original_contract_version"] = []byte(version) //用户自己合约的版本
	parameters["use_chaindata"] = []byte(useCache) //如果链上已经有相同的查询参数做的查询,是否使用链上已有数据返回
	parameters["is_persistence"] = []byte(isPersist) //查询结果是否保存到链上,便于后面可以根据查询参数做查询	

16.2.5. 根据hash来查询数据功能开发(这个是个同步方法,可以直接调用预言机合约直接返回)

调用参数:

	parameters := make(map[string][]byte)
	parameters["method"] = []byte("queryResultByHash")
	parameters["original_contract_name"] = []byte("use_demo")
	parameters["original_contract_version"] = []byte(version)
	parameters["query_hash"] = []byte(qHash) //hash值	

16.2.6. 跨链功能的开发

调用参数:

type CrossChainQuery struct {
	ChainAlias   string         `json:"chain_alias"`   //调用的链id
	ContractName string         `json:"contract_name"` //链上合约名称
	MethodName   string         `json:"method_name"`   //合约的方法
	Params       []KeyValuePair `json:"params"`        //合约所需要参数
}

	parameters := make(map[string][]byte)
	parameters["method"] = []byte("queryCrossChain")
	parameters["original_contract_name"] = []byte("use_demo")
	parameters["original_contract_version"] = []byte(version)
	parameters["cross_query"] = queryMBS //queryMBS为json序列化后的结果
	parameters["use_chaindata"] = []byte(useCache) //如果链上已经有相同的查询参数做的查询,是否使用链上已有数据返回
	parameters["is_persistence"] = []byte(isPersist) //查询结果是否保存到链上,便于后面可以根据查询参数做查询	

16.2.7. 根据指定topic查询数据的开发

调用参数:

	parameters := make(map[string][]byte)
	parameters["method"] = []byte("queryDataByTopic")
	parameters["original_contract_name"] = []byte("use_demo")
	parameters["original_contract_version"] = []byte(version)
	parameters["topic"] = topic
	parameters["use_chaindata"] = []byte(useCache) //如果链上已经有相同的查询参数做的查询,是否使用链上已有数据返回
	parameters["is_persistence"] = []byte(isPersist) //查询结果是否保存到链上,便于后面可以根据查询参数做查询	

16.3. 一个完整的demo合约代码参考

/*
Copyright (C) BABEC. All rights reserved.

SPDX-License-Identifier: Apache-2.0
*/

package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"strings"

	"chainmaker.org/chainmaker/chainmaker-contract-sdk-docker-go/pb/protogo"
	"chainmaker.org/chainmaker/chainmaker-contract-sdk-docker-go/shim"
)

const (
	gOracleContractPkStr = "oracle_contract_pk"
)

type Random struct {
	RandData string  `json:"rand_data"`
	Pi       []byte  `json:"pi"`
	Ratio    float64 `json:"ratio"`
}

type ModelHttp struct {
	Method     string            `json:"method"` //GET,POST
	URL        string            `json:"url"`
	HttpHeader map[string]string `json:"http_header"`
	HttpBody   []byte            `json:"http_body"`

	ConnectionTimeout int `json:"connection_timeout"` //最大连接超时时长,默认30-60s,单位秒级别
	MaxFetchTimeout   int `json:"max_fetch_timeout"`  //最大读取超时时长,默认10s,可以选择为10-60s

	FetchDataType    string `json:"fetch_data_type"`    //json,xml,html
	FetchDataFormula string `json:"fetch_data_formula"` //默认为空,为空则不解析,原样返回。如果非空,则按照. 来分隔取出来结构化的数据,https://goessner.net/articles/JsonPath/(json)
}

type ModelSql struct {
	SqlType     string `json:"sql_type"` //select ,update , insert ,delete
	SqlSentence string `json:"sql_sentence"`
	DataSource  string `json:"data_source"` //数据源
}

type CrossChainQuery struct {
	ChainAlias   string         `json:"chain_alias"`   //调用的链id
	ContractName string         `json:"contract_name"` //链上合约名称
	MethodName   string         `json:"method_name"`   //合约的方法
	Params       []KeyValuePair `json:"params"`        //合约所需要参数
}

type KeyValuePair struct {
	Key   string `json:"key"`
	Value []byte `json:"value"`
}
type UseDemo struct {
}

type OracleQueryResult struct {
	ResultB      []byte `json:"result"`        //查询结果的json序列化
	Code         string `json:"code"`          //错误码
	Message      string `json:"message"`       //错误信息
	ResponseHash string `json:"response_hash"` //返回唯一hash值
}

func (o *UseDemo) Name(stub shim.CMStubInterface) protogo.Response {
	return shim.Success([]byte("use_demo"))
}

func (o *UseDemo) Version(stub shim.CMStubInterface) protogo.Response {
	version, versionErr := stub.GetStateFromKey("version")
	if versionErr != nil {
		stub.Log("Version failed")
		return shim.Error(versionErr.Error())
	}
	return shim.Success([]byte(version))
}

func (o *UseDemo) getOralceContractPk(stub shim.CMStubInterface) (string, error) {
	parameters := make(map[string][]byte)
	parameters["method"] = []byte("queryOracleContractPK")
	resp := stub.CallContract("oracle_contract_v1", "1", parameters)
	if resp.Status != 200 {
		stub.Log("getOralceContractPk error," + resp.String())
		return "", errors.New(resp.String())
	}
	var responeT OracleQueryResult
	uerr := json.Unmarshal(resp.Payload, &responeT)
	if uerr != nil {
		stub.Log("getOralceContractPk unmarshal error")
		return "", errors.New("getOralceContractPk unmarshal error" + uerr.Error())
	}
	return string(responeT.ResultB), nil
}

func (o *UseDemo) InitContract(stub shim.CMStubInterface) protogo.Response {
	// 首先获取一下预言机合约的公钥签名,缓存一下,后续做callback的时候可以校验
	oraclePk, oraclePkErr := o.getOralceContractPk(stub)
	if oraclePkErr != nil {
		return shim.Error(oraclePkErr.Error())
	}
	storeErr := stub.PutStateFromKey(gOracleContractPkStr, oraclePk)
	if storeErr != nil {
		stub.Log("InitContract store oracle_contract_pk error")
		return shim.Error(storeErr.Error())
	}

	version := string(stub.GetArgs()["version"])
	if strings.TrimSpace(version) == "" {
		version = "1"
	}
	err := stub.PutStateFromKey("version", version)
	if err != nil {
		stub.Log("InitContract fail to save version")
		return shim.Error("fail to save version")
	}
	stub.Log(fmt.Sprintf("InitContract version(%s) ", version))
	return shim.Success([]byte("Init Success"))
}

func (o *UseDemo) InvokeContract(stub shim.CMStubInterface) protogo.Response {

	method := string(stub.GetArgs()["method"])
	stub.Log("custom " + method)
	switch method {
	case "query_http":
		return o.QueryHttp(stub)
	case "query_mysql":
		return o.QueryMysql(stub)
	case "get_vrf":
		return o.GetVRF(stub)
	case "oracle_callback":
		return o.OracleCallBack(stub)
	case "name":
		return o.Name(stub)
	case "version":
		return o.Version(stub)
	case "query_hash":
		return o.QueryUseHash(stub)
	case "query_cross":
		return o.QueryCross(stub)
	// case "register_topic":
	// 	return o.RegisterTopic(stub)
	case "query_topic":
		return o.QueryTopic(stub)
	default:
		stub.Log("InvokeContract invalid method, method is " + method)
		return shim.Error("invalid method")
	}

}

func (o *UseDemo) QueryTopic(stub shim.CMStubInterface) protogo.Response {
	version, versionErr := stub.GetStateFromKey("version")
	if versionErr != nil {
		stub.Log("Version failed")
		return shim.Error(versionErr.Error())
	}
	args := stub.GetArgs()
	topic := args["topic"]
	useCache := string(args["use_chaindata"])
	isPersist := string(args["is_persist"])

	parameters := make(map[string][]byte)
	parameters["method"] = []byte("queryDataByTopic")
	parameters["original_contract_name"] = []byte("use_demo")
	parameters["original_contract_version"] = []byte(version)
	parameters["use_chaindata"] = []byte(useCache)
	parameters["is_persistence"] = []byte(isPersist)
	parameters["topic"] = topic
	resp := stub.CallContract("oracle_contract_v1", "1", parameters)
	if resp.Status != 200 {
		stub.Log("QueryTopic error," + resp.String())
		return shim.Error(resp.String())
	}

	stub.Log("QueryTopic response," + resp.String())
	var responeT OracleQueryResult
	uerr := json.Unmarshal(resp.Payload, &responeT)
	if uerr != nil {
		stub.Log("QueryTopic unmarshal error")
	}
	if responeT.Code == "201" {
		//同步调用
		stub.Log("QueryTopic sync," + string(responeT.ResultB))
		return shim.Success(responeT.ResultB)
	}
	stub.Log("QueryTopic " + string(responeT.ResponseHash))
	stub.PutStateFromKey(string(responeT.ResponseHash), string(topic))
	//stub.PutStateByte(string(responeT.ResponseHash), "parameter", sqlBs) //存根一下
	return shim.Success([]byte("QueryTopic ok"))
}

func (o *UseDemo) GetVRF(stub shim.CMStubInterface) protogo.Response {
	version, versionErr := stub.GetStateFromKey("version")
	if versionErr != nil {
		stub.Log("Version failed")
		return shim.Error(versionErr.Error())
	}

	args := stub.GetArgs()
	alpha := string(args["alpha"])

	parameters := make(map[string][]byte)

	parameters["alpha"] = []byte(alpha)
	parameters["method"] = []byte("getVrf")
	parameters["original_contract_name"] = []byte("use_demo")
	parameters["original_contract_version"] = []byte(version)

	//stub.EmitEvent("http_query", []string{url, "aac"})
	resp := stub.CallContract("oracle_contract_v1", "1", parameters)
	if resp.Status != 200 {
		stub.Log("GetVRF error," + resp.String())
		return shim.Error(resp.String())
	}
	stub.Log("GetVRF resp" + resp.String())
	var responeT OracleQueryResult
	uerr := json.Unmarshal(resp.Payload, &responeT)
	if uerr != nil {
		stub.Log("GetVRF unmarshal error")
	}
	stub.Log("get_vrf_responset " + string(responeT.ResponseHash))
	stub.PutStateFromKey(string(responeT.ResponseHash), string(alpha))
	//stub.PutStateByte(string(responeT.ResponseHash), "parameter", []byte(alpha)) //存根一下
	return shim.Success([]byte(resp.String()))
}

func (o *UseDemo) QueryHttp(stub shim.CMStubInterface) protogo.Response {
	version, versionErr := stub.GetStateFromKey("version")
	if versionErr != nil {
		stub.Log("Version failed")
		return shim.Error(versionErr.Error())
	}
	args := stub.GetArgs()
	useCache := string(args["use_chaindata"])
	isPersist := string(args["is_persist"])

	url := string(args["url"])
	fetchType := string(args["fetch_data_type"])
	fetchFormular := string(args["fetch_data_formular"])
	parameters := make(map[string][]byte)
	httpQuerys := ModelHttp{
		Method:            "GET",
		URL:               url, //"https://www.baidu.com/baidu?wd=chainmaker",,
		ConnectionTimeout: 30,
		MaxFetchTimeout:   30,
		FetchDataType:     fetchType,
		FetchDataFormula:  fetchFormular,
	}
	if fetchType == "html" {
		httpQuerys.HttpHeader = map[string]string{
			"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3776.0 Safari/537.36",
		}
	}
	httpBs, _ := json.Marshal(httpQuerys)

	parameters["http_query"] = httpBs
	parameters["method"] = []byte("queryHttp")
	parameters["original_contract_name"] = []byte("use_demo")
	parameters["original_contract_version"] = []byte(version)
	parameters["use_chaindata"] = []byte(useCache)
	parameters["is_persistence"] = []byte(isPersist)
	resp := stub.CallContract("oracle_contract_v1", "1", parameters)
	if resp.Status != 200 {
		stub.Log("query_http error," + resp.String())
		return shim.Error(resp.String())
	}
	stub.Log("query_http resp," + resp.String())
	var responeT OracleQueryResult
	uerr := json.Unmarshal(resp.Payload, &responeT)
	if uerr != nil {
		stub.Log("GetVRF unmarshal error")
	}
	if responeT.Code == "201" {
		//同步调用结果
		stub.Log("QueryHttp sync," + string(responeT.ResultB))
		return shim.Success(responeT.ResultB)
	}
	stub.Log("query_http_responset " + string(responeT.ResponseHash))
	stub.PutStateFromKey(string(responeT.ResponseHash), string(httpBs))
	//stub.PutStateByte(string(responeT.ResponseHash), "parameter", httpBs) //存根一下
	return shim.Success([]byte(resp.String()))

}

func (o *UseDemo) QueryCross(stub shim.CMStubInterface) protogo.Response {
	//订制化查询hash函数
	version, versionErr := stub.GetStateFromKey("version")
	if versionErr != nil {
		stub.Log("Version failed")
		return shim.Error(versionErr.Error())
	}
	args := stub.GetArgs()
	chainA := string(args["chain_alias"])
	name := string(args["contract_name"])
	mName := string(args["method_name"])
	useCache := string(args["use_chaindata"])
	isPersist := string(args["is_persist"])
	qHash := string(args["query_hash"])
	var kvp []KeyValuePair
	kvp = append(kvp, KeyValuePair{
		Key:   "query_hash",
		Value: []byte(qHash),
	})
	var queryM CrossChainQuery
	queryM.ChainAlias = chainA
	queryM.ContractName = name
	queryM.MethodName = mName
	queryM.Params = kvp
	queryMBS, _ := json.Marshal(queryM)
	parameters := make(map[string][]byte)
	parameters["method"] = []byte("queryCrossChain")
	parameters["original_contract_name"] = []byte("use_demo")
	parameters["original_contract_version"] = []byte(version)
	parameters["use_chaindata"] = []byte(useCache)
	parameters["is_persistence"] = []byte(isPersist)
	parameters["cross_query"] = queryMBS
	resp := stub.CallContract("oracle_contract_v1", "1", parameters)
	if resp.Status != 200 {
		stub.Log("QueryCross error," + resp.String())
		return shim.Error(resp.String())
	}

	stub.Log("QueryCross response," + resp.String())
	var responeT OracleQueryResult
	uerr := json.Unmarshal(resp.Payload, &responeT)
	if uerr != nil {
		stub.Log("QueryCross unmarshal error")
	}
	if responeT.Code == "201" {
		stub.Log("QueryCross sync," + string(responeT.ResultB))
		return shim.Success(responeT.ResultB)
	}
	stub.Log("QueryCross " + string(responeT.ResponseHash))
	stub.PutStateFromKey(string(responeT.ResponseHash), string(queryMBS))
	//stub.PutStateByte(string(responeT.ResponseHash), "parameter", sqlBs) //存根一下
	return shim.Success([]byte("QueryCross ok"))

}

func (o *UseDemo) QueryUseHash(stub shim.CMStubInterface) protogo.Response {
	args := stub.GetArgs()
	qHash := string(args["query_hash"])
	version, versionErr := stub.GetStateFromKey("version")
	if versionErr != nil {
		stub.Log("Version failed")
		return shim.Error(versionErr.Error())
	}
	parameters := make(map[string][]byte)
	parameters["method"] = []byte("queryResultByHash")
	parameters["original_contract_name"] = []byte("use_demo")
	parameters["original_contract_version"] = []byte(version)
	parameters["query_hash"] = []byte(qHash)
	resp := stub.CallContract("oracle_contract_v1", "1", parameters)
	if resp.Status != 200 {
		stub.Log("QueryUseHash error," + resp.String())
		return shim.Error(resp.String())
	}
	var responeT OracleQueryResult
	uerr := json.Unmarshal(resp.Payload, &responeT)
	if uerr != nil {
		stub.Log("QueryCross unmarshal error")
	}
	if responeT.Code == "201" && responeT.Message == "success" {
		stub.Log("QueryUseHash sync," + string(responeT.ResultB))
		return shim.Success(responeT.ResultB)
	} else {
		return shim.Success(resp.Payload)
	}

}

func (o *UseDemo) QueryMysql(stub shim.CMStubInterface) protogo.Response {
	version, versionErr := stub.GetStateFromKey("version")
	if versionErr != nil {
		stub.Log("Version failed")
		return shim.Error(versionErr.Error())
	}
	args := stub.GetArgs()
	sqlType := string(args["sql_type"])
	sqlSentence := string(args["sql_sentence"])
	dataSource := string(args["data_source"])
	useCache := string(args["use_chaindata"])
	isPersist := string(args["is_persist"])
	parameters := make(map[string][]byte)
	sqlQuerys := ModelSql{
		SqlType:     sqlType,
		SqlSentence: sqlSentence,
		DataSource:  dataSource,
	}
	sqlBs, _ := json.Marshal(sqlQuerys)
	parameters["mysql_query"] = sqlBs
	parameters["method"] = []byte("queryMysql")
	parameters["original_contract_name"] = []byte("use_demo")
	parameters["original_contract_version"] = []byte(version)
	parameters["use_chaindata"] = []byte(useCache)
	parameters["is_persistence"] = []byte(isPersist)
	resp := stub.CallContract("oracle_contract_v1", "1", parameters)
	if resp.Status != 200 {
		stub.Log("QueryMysql error," + resp.String())
		return shim.Error(resp.String())
	}

	stub.Log("QueryMysql response," + resp.String())
	var responeT OracleQueryResult
	uerr := json.Unmarshal(resp.Payload, &responeT)
	if uerr != nil {
		stub.Log("QueryMysql unmarshal error")
	}
	if responeT.Code == "201" { //同步返回结果
		stub.Log("QueryMysql sync," + string(responeT.ResultB))
		return shim.Success(responeT.ResultB)
	}
	stub.Log("query_mysql_responset " + string(responeT.ResponseHash))
	stub.PutStateFromKey(string(responeT.ResponseHash), string(sqlBs))
	//stub.PutStateByte(string(responeT.ResponseHash), "parameter", sqlBs) //存根一下
	return shim.Success(resp.Payload)
}

func (o *UseDemo) OracleCallBack(stub shim.CMStubInterface) protogo.Response {
	//首先需要做一下校验,校验一下调用这个函数的交易发起者pk是否是预言机合约;不是的话,不允许调用
	senderPk, senderPkErr := stub.GetSenderPk()
	if senderPkErr != nil {
		stub.Log("OracleCallBack getSenderPk error , " + senderPkErr.Error())
		return shim.Error(senderPkErr.Error())
	}
	storePk, storePkErr := stub.GetStateFromKey(gOracleContractPkStr)
	if storePkErr != nil {
		stub.Log("OracleCallBack getStateFromKey error , " + storePkErr.Error())
		return shim.Error(storePkErr.Error())
	}
	if storePk != senderPk {
		return shim.Error("OracleCallBack failed , tx senderpk not match")
	}
	//
	args := stub.GetArgs()
	results := string(args["result"])
	originalMethod := string(args["original_method"])
	responseHash := args["response_hash"]
	code := string(args["code"])
	message := string(args["message"])
	value, valueErr := stub.GetStateFromKey(string(responseHash))
	if valueErr != nil {
		stub.Log("callback got key error ")
	}
	//parameters, _ := stub.GetStateByte(string(responseHash), "parameter") //寻找对应关系
	stub.Log(fmt.Sprintf("originalMethod(%s) , callback result (%s) ,code(%s), message(%s), parameter(%s) , responseHash(%s) \n", originalMethod, results,
		code, message, value, string(responseHash)))
	if originalMethod == "get_vrf" {
		var vrfR Random
		json.Unmarshal(args["result"], &vrfR)
		parameters := make(map[string][]byte)
		parameters["pi"] = vrfR.Pi
		parameters["alpha"] = []byte(value)
		parameters["method"] = []byte("verifyVrf")
		verifyResp := stub.CallContract("oracle_contract_v1", "1", parameters)
		if verifyResp.Status != 200 {
			stub.Log("verify vrf error, " + verifyResp.String())
			return shim.Error("verify vrf error")
		}
		var verifyT OracleQueryResult
		json.Unmarshal(verifyResp.Payload, &verifyT)
		if string(verifyT.ResultB) != "true" {
			stub.Log("verify vrf false")
			return shim.Error("verify vrf false")
		}
	}
	return shim.Success([]byte("callback done"))
}

func main() {

	err := shim.Start(new(UseDemo))
	if err != nil {
		panic(err)
	}
}

16.4. 通过接口对预言机进行管理和简化开发

16.4.1. 安装预言机合约接口

/v1/install_contract 
方法: POST
格式: Form表单
header: admin_auth_token
参数:
    1. chain_alias 链id
    2. contract_name 合约名称
    3. contract_version 合约版本(整形数字,从1开始)
    4. contract_file 合约文件(7z文件)
    5. runtime 合约格式(暂时只支持DOCKER_GO)
返回参数:
    格式: json
    code: 错误码(0 为成功)
    message: 错误信息(默认为success)
    data: 返回合约信息

16.4.2. 查询已安装合约接口

/v1/list_contracts 
方法: GET
格式: query参数
header: assist_auth_token
参数:
    1. chain_alias 链id
返回参数:
    格式: json
    code: 错误码(0 为成功)
    message: 错误信息(默认为success)
    data: 返回合约信息列表

16.4.3. 升级合约接口

/v1/upgrade_contract 
方法: POST
header: admin_auth_token
格式: Form表单
参数:
    1. chain_alias 链id
    2. contract_name 合约名称
    3. contract_version 合约版本(整形数字,从1开始)
    4. contract_file 合约文件(7z文件)
    5. runtime 合约格式(暂时只支持DOCKER_GO)
返回参数:
    格式: json
    code: 错误码(0 为成功)
    message: 错误信息(默认为success)
    data: 返回合约信息

16.4.4. 生成VRF接口

/v1/vrf/get_random
方法:POST
header: assist_auth_token
格式:Form表单
参数:
    1. alpha 用户输入随机数字
返回参数:
    格式: json
    code: 错误码(0 为成功)
    message: 错误信息(默认为success)
    data: 返回VRF信息  
    

16.4.5. 验证VRF接口

/v1/vrf/verify
方法:POST
header: assist_auth_token
格式:json
参数:
    1. alpha 用户输入随机数字
    2. pi 证据
返回参数:
    格式: json
    code: 错误码(0 为成功)
    message: 错误信息(默认为success)
    data: 是否验证通过(true/false)  
    

16.4.6. 查询预言机公钥接口

/v1/vrf/get_public_key
方法:GET
header: assist_auth_token
返回参数:
    格式: json
    code: 错误码(0 为成功)
    message: 错误信息(默认为success)
    data: 返回公钥

16.4.7. 服务是否可触达接口

/v1/health 
方法: GET
格式: query参数
header: assist_auth_token
返回参数:
    格式: json
    code: 错误码(0 为成功)
    message: 错误信息(默认为success)

16.4.8. 查询mysql数据接口

/v1/query/query_mysql 
方法: POST
格式: Json
header: assist_auth_token
参数:
type ModelSql struct {
	SqlType     string `json:"sql_type"` //select
	SqlSentence string `json:"sql_sentence"`
	DataSource  string `json:"data_source"` //数据源
}的序列化json数据
返回参数:
    格式: json
    code: 错误码(0 为成功)
    message: 错误信息(默认为success)
    data: 返回合约信息

16.4.9. 查询http接口数据接口

/v1/query/query_http 
方法: POST
格式: Json
header: assist_auth_token
参数:
type ModelHttp struct {
	URL               string            `json:"url"`
	HttpHeader        map[string]string `json:"http_header"`
	HttpBody          []byte            `json:"http_body"`
	ConnectionTimeout int               `json:"connection_timeout"` //最大连接超时时长,默认30s,单位秒级别
	MaxRetry          int               `json:"max_retry"`          //最大连接试错次数,默认3次
	MaxFetchTimeout   int               `json:"max_fetch_timeout"`  //最大读取超时时长,默认10s,可以选择为10-60s
	FetchDataType     string            `json:"fetch_data_type"`    //json,xml,html
	FetchDataFormula  string            `json:"fetch_data_formula"` //默认为空,为空则不解析,原样返回。如果非空,则按照. 来分隔取出来结构化的数据
}的序列化json数据
返回参数:
    格式: json
    code: 错误码(0 为成功)
    message: 错误信息(默认为success)
    data: 返回合约信息

16.4.10. 跨链查询数据接口

/v1/query/query_cross 
方法: POST
格式: Json
header: assist_auth_token
参数:
# CrossChainQuery 合约跨链查询模型
type CrossChainQuery struct {
	ChainAlias   string         `json:"chain_alias"` //调用的链id
	ContractName string         `json:"contract_name"`
	MethodName   string         `json:"method_name"`
	Params       []KeyValuePair `json:"params"`
}

# KeyValuePair 合约传参数模型
type KeyValuePair struct {
	Key   string `json:"key"`
	Value []byte `json:"value"`
}的序列化json数据
返回参数:
    格式: json
    code: 错误码(0 为成功)
    message: 错误信息(默认为success)
    data: 返回合约信息