CNS(Contract Name Service)服务

一.概述

1.合约调用概述

调用合约的流程包括: 编写合约、编译合约、部署合约。以一个简单的合约HelloWorld.sol为例子:

  1. //HelloWorld.sol路径为FISCO-BCOS/tool/HelloWorld.sol
  2. pragma solidity ^0.4.2;
  3. contract HelloWorld{
  4. string name;
  5. function HelloWorld(){
  6. name="Hi,Welcome!";
  7. }
  8. function get()constant returns(string){
  9. return name;
  10. }
  11. function set(string n){
  12. name=n;
  13. }
  14. }

对合约进行编译之后可以获取到合约接口abi的描述,数据如下:

  1. [
  2. {
  3. "constant": false,
  4. "inputs": [
  5. {
  6. "name": "n",
  7. "type": "string"
  8. }
  9. ],
  10. "name": "set",
  11. "outputs": [
  12.  
  13. ],
  14. "payable": false,
  15. "stateMutability": "nonpayable",
  16. "type": "function"
  17. },
  18. {
  19. "constant": true,
  20. "inputs": [
  21.  
  22. ],
  23. "name": "get",
  24. "outputs": [
  25. {
  26. "name": "",
  27. "type": "string"
  28. }
  29. ],
  30. "payable": false,
  31. "stateMutability": "view",
  32. "type": "function"
  33. },
  34. {
  35. "inputs": [
  36.  
  37. ],
  38. "payable": false,
  39. "stateMutability": "nonpayable",
  40. "type": "constructor"
  41. }
  42. ]

然后将合约部署到区块链,可以获取到一个地址address,比如:0x269ab4bc23b07efeb3c3fd52eecfc4cbe6a50859。最后使用address结合abi,就可以实现对合约的调用,各种SDK工具的使用方式都有所不同,但是本质上都是对address与abi使用的封装。

2. CNS简介

从合约调用流程可以看出来,调用流程必须的元素是合约ABI以及部署合约地址address。这种方式存在以下的问题:

  • 合约ABI描述是个很长的JSON字符串, 是调用端需要, 对使用者来说并不友好。
  • 合约地址是个魔数, 不方便记忆, 要谨慎维护, 丢失后会导致合约不可访问。
  • 在合约每次重新部署时, 调用方都需要更新合约地址。
  • 不便于进行版本管理以及合约灰度升级。CNS合约命名服务,提供一种由命名到合约接口调用的映射关系。CNS中,调用合约接口时,传入合约映射的name,接口名称以及参数信息。在底层框架的CNS Manager模块维护name与合约信息的映射关系,将根据调用传入的name 、接口、参数, 转换底层EVM需要的字节码供EVM调用。

下面给出一个图示来说明下CNS服务的优势:../../../_images/pic11.jpg

  • 不在需要维护并不友好的合约ABI和合约地址address。
  • 调用方式更简单友好,只需要合约映射的CNS名称,调用接口名称,参数信息。
  • 内置的版本管理特性, 为合约提供了灰度升级的可能性。

二.实现

1. 总体框架

../../../_images/pic21.jpg在整个框架中, 命名服务模块提供命名服务, 客户端请求RPC调用合约服务的交易, 交易框架会首先访问合约命名服务模块, 从而解析出要访问的真实合约信息, 构造合约调用需要的信息, 进而对业务合约发出调用,并访问结果给客户端。

2. 主要模块

a. 管理合约模块

在管理合约中保存命名服务中name与合约信息的映射关系,合约具体信息包含合约地址、abi、版本号等,并且提供接口供外部辅助工具cns_manager.js实现添加、更新、覆盖、重置功能。同时,底层交易框架内存中会有一份该合约内容的备份,在该合约内容发生变动时,内存同步更新。

  • 当前CNS中实现的映射关系为 : 合约名+合约版本号 => 合约详情(abi 合约地址等)
  • 合约实现: systemcontract/ContractAbiMgr.sol
  • 辅助合约: ContractBase.sol(位于tool/ContractBase.sol)
  • 对部署的合约进行多版本版本管理,可以让合约继承ContractBase.sol,在构造函数中调用ContractBase.sol的构造函数初始化version成员。
  • 注意:ContractAbiMgr合约在系统合约中维护,所以需要使用CNS服务时需要首先部署系统合约。

b. 辅助工具

调用管理合约提供的接口, 提供添加、更新、查询、重置功能。

  • 工具: tool/cns_manager.js
  1. babel-node cns_manager.js
  2. cns_manager.js Usage:
  3. babel-node cns_manager.js get contractName [contractVersion]
  4. babel-node cns_manager.js add contractName
  5. babel-node cns_manager.js update contractName
  6. babel-node cns_manager.js list [simple]
  7. babel-node cns_manager.js historylist contractName [contractVersion] [simple]
  8. babel-node cns_manager.js reset contractName [contractVersion] index

功能介绍:

  • 命令 : add参数 : contractName 合约名功能 : 添加contractName的信息到管理合约中注意 : 如果管理合约中contractName对应的信息已经存在,会操作失败。此时可以 1. 更新当前合约的版本号,使用CNS方式调用时,指定版本号 2. 执行update操作,强行覆盖当前信息。
  1. //第一次add Test成功
  2. babel-node cns_manager.js add Test
  3. cns add operation => cns_name = Test
  4. cns_name =>Test
  5. contract =>Test
  6. version =>
  7. address =>0x233c777fccb9897ad5537d810068f9c6a4344e4a
  8. abi =>[{"constant":false,"inputs":[{"name":"num","type":"uint256"}],"name":"trans","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"Ok","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]
  9.  
  10. //第二次add,失败
  11. babel-node cns_manager.js add Test
  12. cns_manager.js ........................Begin........................
  13. [WARNING] cns add operation failed , ====> contract => Test version => is already exist. you can update it or change its version.
  • 命令 : get参数 : 1. contractName 合约名 2. contractVersion 版本号[可省略]功能 : 获取contractName对应contractVersion版本在管理合约中的信息
  1. babel-node cns_manager.js get HelloWorld
  2. cns_manager.js ........................Begin........................
  3. ====> contract => HelloWorld ,version =>
  4. contract = HelloWorld
  5. version =
  6. address = 0x269ab4bc23b07efeb3c3fd52eecfc4cbe6a50859
  7. timestamp = 1516866720115 => 2018/1/25 15:52:0:115
  8. abi = [{"constant":false,"inputs":[{"name":"n","type":"string"}],"name":"set","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]
  • 命令 : update参数 : contractName 合约名功能 : 更新contractName在管理合约中的信息注意 : 当contractName对应版本contractVersion在管理合约中不存在时会update失败,此时可以先执行add操作;被覆盖掉的信息可以通过historylist命令查询到,通过reset命令恢复。
  1. babel-node cns_manager.js update Test
  2. cns_manager.js ........................Begin........................
  3. ====> Are you sure update the cns of the contract ?(Y/N)
  4. Y
  5. cns update operation => cns_name = Test
  6. cns_name =>Test
  7. contract =>Test
  8. version =>
  9. address =>0x233c777fccb9897ad5537d810068f9c6a4344e4a
  10. abi =>[{"constant":false,"inputs":[{"name":"num","type":"uint256"}],"name":"trans","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"Ok","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]
  11. 发送交易成功: 0x1d3caff1fba49f5ad8af3d195999454d01c64d236d9ac3ba91350dd543b10c13
  • 命令 : list参数 : [simple]功能 : 列出管理合约中所有的信息,没有simple参数时,打印合约的详情,否则只打印合约名称跟版本号。
  1. babel-node cns_manager.js list simple
  2. cns_manager.js ........................Begin........................
  3. cns total count => 11
  4. 0. contract = ContractAbiMgr ,version =
  5. 1. contract = SystemProxy ,version =
  6. 2. contract = TransactionFilterChain ,version =
  7. 3. contract = AuthorityFilter ,version =
  8. 4. contract = Group ,version =
  9. 5. contract = CAAction ,version =
  10. 6. contract = ConfigAction ,version =
  11. 7. contract = NodeAction ,version =
  12. 8. contract = HelloWorld ,version =
  13. 9. contract = Ok ,version =
  14. 10. contract = Test ,version =
  • 命令 : historylist参数 : contractName 合约名称 contractVersion 合约版本号[可省略]功能 : 列出contrcactName对应版本号contractVersion被update操作覆盖的所有合约信息
  1. babel-node cns_manager.js historylist HelloWorld
  2. cns_manager.js ........................Begin........................
  3. cns history total count => 3
  4. ====> cns history list index = 0 <====
  5. contract = HelloWorld
  6. version =
  7. address = 0x1d2047204130de907799adaea85c511c7ce85b6d
  8. timestamp = 1516865606159 => 2018/1/25 15:33:26:159
  9. abi = [{"constant":false,"inputs":[{"name":"n","type":"string"}],"name":"set","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]
  10. ====> cns history list index = 1 <====
  11. contract = HelloWorld
  12. version =
  13. address = 0x9c3fb4dd0a3fc5e1ea86ed3d3271b173a7084f24
  14. timestamp = 1516866516542 => 2018/1/25 15:48:36:542
  15. abi = [{"constant":false,"inputs":[{"name":"n","type":"string"}],"name":"set","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]
  16. ====> cns history list index = 2 <====
  17. contract = HelloWorld
  18. version =
  19. address = 0x1d2047204130de907799adaea85c511c7ce85b6d
  20. timestamp = 1516866595160 => 2018/1/25 15:49:55:160
  21. abi = [{"constant":false,"inputs":[{"name":"n","type":"string"}],"name":"set","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]
  • 命令 : reset参数 : 1. contractName 合约名称 2. contractVersion 合约版本号[可省略] 3. index 索引功能 : 将被覆盖的信息恢复,index为historylist查询到的索引

c. RPC接口

底层rpc接口的修改,支持CNS方式的调用:

注意:只是修改了rpc的接口,原来的请求方式仍然兼容。rpc的格式详情参考:https://github.com/ethereum/wiki/wiki/JSON-RPC
  • eth_call
  1. 请求:
  2. {
  3. "jsonrpc": "2.0",
  4. "method": "eth_call",
  5. "params": [
  6. {
  7. "data": {
  8. "contract": "", //调用合约名称
  9. "version": "", //调用合约的版本号
  10. "func": "", //调用合约的接口
  11. "params": [ //参数信息
  12.  
  13. ]
  14. }
  15. },
  16. "latest"
  17. ],
  18. "id": 1
  19. }
  20.  
  21. 返回:
  22. {
  23. "id": 1,
  24. "jsonrpc": "2.0",
  25. "result": {
  26. "result": [], //返回结果,格式为json数组
  27. "ret_code": 0,
  28. "ret_msg": "success!"
  29. }
  30. }
  • eth_sendTransaction
  1. 请求:
  2. {
  3. "jsonrpc": "2.0",
  4. "method": "eth_sendTransaction",
  5. "params": [
  6. {
  7. "data": {
  8. "contract": "", //调用合约名称
  9. "version": "", //调用合约的版本号
  10. "func": "", //调用合约的接口
  11. "params": [ //参数
  12. ]
  13. },
  14. "randomid": "2"
  15. }
  16. ],
  17. "id": 1
  18. }
  19.  
  20. 返回:
  21. {
  22. "id": 1,
  23. "jsonrpc": "2.0",
  24. "result": "" //交易hash
  25. }
  • eth_sendRawTransactionrpc请求以及返回格式跟之前完全相同,不同为之前rlp编码data字段为十六进制字符串,现在data的值改为:
  1. "data": {
  2. "contract": "", //调用合约名称
  3. "version": "", //调用合约的版本号
  4. "func": "", //调用合约的接口
  5. "params": [ //参数信息
  6.  
  7. ]
  8. }

d. RPC接口JS封装

路径:web3lib/web3sync.js接口:

  1. callByNameService
  2. sendRawTransactionByNameService

三.使用例子

本模块提供一些CNS一些场景下使用的例子, 供大家参考

  1. // 测试合约
  2. // 路径 tool/HelloWorld.sol
  3. pragma solidity ^0.4.4;
  4. contract HelloWorld{
  5. string name;
  6. function HelloWorld(){
  7. name="Hi,Welcome!";
  8. }
  9. function get()constant public returns(string){
  10. return name;
  11. }
  12. function set(string n) public{
  13. name=n;
  14. }
  15. }
  • 合约部署:babel-node deploy.js HelloWorld
在depoy.js中, 合约部署成功时会默认调用cns_mangager add功能, 而且会默认认为文件名与合约名相同, 如果添加失败, 需要后续部署的人自己决策:
  • 实际上文件名与合约名不相同, 重新调用cns_manager add
  • 只是测试合约的部署,不需要处理
  • 同一个合约修改bug或者因为其他原因需要升级, 此时可以执行update操作
  • 当前已经add的合约仍然需要CNS方式调用, 可以修改合约的版本号(参考多版本部署)。
  1. //成功例子
  2. babel-node deploy.js Test0
  3. deploy.js ........................Start........................
  4. Soc File :Test0
  5. Test0编译成功!
  6. Test0合约地址 0xfc7055a9dc68ff79a58ce4f504d8c780505b2267
  7. Test0部署成功!
  8. cns add operation => cns_name = Test0
  9. cns_name =>Test0
  10. contract =>Test0
  11. version =>
  12. address =>0xfc7055a9dc68ff79a58ce4f504d8c780505b2267
  13. abi =>[{"constant":false,"inputs":[{"name":"n","type":"string"}],"name":"set","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"HelloWorld","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]
  14. 发送交易成功: 0x84d1e6b16c58e3571f79e80588472ab8d12779234e75ceed4ac592ad1d653086
  15.  
  16. //失败例子,表示该合约已经存在对应信息
  17. babel-node deploy.js HelloWorld
  18. deploy.js ........................Start........................
  19. Soc File :HelloWorld
  20. HelloWorld编译成功!
  21. HelloWorld合约地址 0xc3869f3d9a5fc728de82cc9c807e85b77259aa3a
  22. HelloWorld部署成功!
  23. [WARNING] cns add operation failed , ====> contract => HelloWorld version => is already exist. you can update it or change its version.
  24.  

-多版本部署对于add操作时,因为添加的合约对应的版本号已经存在时, 则会添加失败, 此时可以更新合约的版本号。继承ContractBase.sol指定版本号。

  1. pragma solidity ^0.4.4;
  2. import "ContractBase.sol";
  3. contract HelloWorld is ContractBase("v-1.0"){
  4. string name;
  5. function HelloWorld(){
  6. name="Hi,Welcome!";
  7. }
  8. function get()constant public returns(string){
  9. return name;
  10. }
  11. function set(string n) public{
  12. name=n;
  13. }
  14. }

再次部署

  1. babel-node deploy.js HelloWorld
  2. deploy.js ........................Start........................
  3. Soc File :HelloWorld
  4. HelloWorld编译成功!
  5. HelloWorld合约地址 0x027d156c260110023e5bd918cc243ac12be45b17
  6. HelloWorld部署成功!
  7. cns add operation => cns_name = HelloWorld/v-1.0
  8. cns_name =>HelloWorld/v-1.0
  9. contract =>HelloWorld
  10. version =>v-1.0
  11. address =>0x027d156c260110023e5bd918cc243ac12be45b17
  12. abi =>[{"constant":true,"inputs":[],"name":"getVersion","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"n","type":"string"}],"name":"set","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"version_para","type":"string"}],"name":"setVersion","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]
  13. 发送交易成功: 0x9a409003f5a17220809dd8e1324a36a425acaf194efd3ef1f772bbf7b49ee67c

此时合约版本号为: v-1.0

  • RPC调用接口
  1. 1. 调用HelloWorld默认版本(即没有指定版本号)的set接口
  2. curl -X POST --data '{"jsonrpc":"2.0","method":"eth_sendTransaction","params":[{"data":{"contract":"HelloWorld","version":"","func":"set","params":["call defaut version"]},"randomid":"3"}],"id":1}' "http://127.0.0.1:8746"
  3.  
  4. {"id":1,"jsonrpc":"2.0","result":"0x77218708a73aa8c17fb9370a29254baa8f504e71b12d01d90eae0b2ef9818172"}
  5.  
  6. 2. 调用HelloWorld默认版本(即没有指定版本号)的get接口
  7. curl -X POST --data '{"jsonrpc":"2.0","method":"eth_call","params":[{"data":{"contract":"HelloWorld","version":"","func":"get","params":[]}},"latest"],"id":1}' "http://127.0.0.1:8746"
  8.  
  9. {"id":1,"jsonrpc":"2.0","result":"[\"call defaut version\"]\n"}
  10.  
  11. 3. 调用HelloWorld v-1.0版本的set接口
  12. curl -X POST --data '{"jsonrpc":"2.0","method":"eth_sendTransaction","params":[{"data":{"contract":"HelloWorld","version":"v-1.0","func":"set","params":["call v-1.0 version"]},"randomid":"4"}],"id":1}' "http://127.0.0.1:8746"
  13.  
  14. {"id":1,"jsonrpc":"2.0","result":"0xf43349d7be554fd332e8e4eb0c69e23292ffa8d127b0500c21109b60784aaa1d"}
  15.  
  16. 4. 调用HelloWorld v-1.0版本的get接口
  17. curl -X POST --data '{"jsonrpc":"2.0","method":"eth_call","params":[{"data":{"contract":"HelloWorld","version":"v-1.0","func":"get","params":[]}},"latest"],"id":1}' "http://127.0.0.1:8746"
  18.  
  19. {"id":1,"jsonrpc":"2.0","result":"[\"call v-1.0 version\"]\n"}
  • 合约升级如果合约需要升级的情况下, 可以执行执行update操作。对HelloWorld进行升级, 首先重新部署, 因为HelloWorld之前被cns_manager添加, 所以会提示添加失败, 然后执行update操作。
  1. babel-node cns_manager.js update HelloWorld
  2. cns_manager.js ........................Begin........................
  3. ====> Are you sure update the cns of the contract ?(Y/N)
  4. Y
  5. cns update operation => cns_name = HelloWorld
  6. cns_name =>HelloWorld
  7. contract =>HelloWorld
  8. version =>
  9. address =>0x93d62e961a6801d3f614a5add207cdf45b0ff654
  10. abi =>[{"constant":false,"inputs":[{"name":"n","type":"string"}],"name":"set","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]
  11. 发送交易成功: 0xc8ee384185a1aaa3817474d6db6394ff6871a7bc56a15e564e7b1f57c8bfda1a
  12.  
  13. 再调用get接口:
  14. curl -X POST --data '{"jsonrpc":"2.0","method":"eth_call","params":[{"data":{"contract":"HelloWorld","version":"","func":"get","params":[]}},"latest"],"id":1}' "http://127.0.0.1:8746"
  15. {"id":1,"jsonrpc":"2.0","result":"[\"Hi,Welcome!\"]\n"}
  16.  
  17. 返回 'Hi,Welcome!'
  18. 说明当前调用的合约就是刚才部署的新合约。
  • CNS合约重置update之后, 需要将原来的合约找回, 可以通过reset进行。首先查找当前合约对应版本有多少update被覆盖的合约。
  1. babel-node cns_manager.js historylist HelloWorld
  2. cns_manager.js ........................Begin........................
  3. cns history total count => 4
  4. ====> cns history list index = 0 <====
  5. contract = HelloWorld
  6. version =
  7. address = 0x1d2047204130de907799adaea85c511c7ce85b6d
  8. timestamp = 1516865606159 => 2018/1/25 15:33:26:159
  9. abi = [{"constant":false,"inputs":[{"name":"n","type":"string"}],"name":"set","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]
  10. ====> cns history list index = 1 <====
  11. contract = HelloWorld
  12. version =
  13. address = 0x9c3fb4dd0a3fc5e1ea86ed3d3271b173a7084f24
  14. timestamp = 1516866516542 => 2018/1/25 15:48:36:542
  15. abi = [{"constant":false,"inputs":[{"name":"n","type":"string"}],"name":"set","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]
  16. ====> cns history list index = 2 <====
  17. contract = HelloWorld
  18. version =
  19. address = 0x1d2047204130de907799adaea85c511c7ce85b6d
  20. timestamp = 1516866595160 => 2018/1/25 15:49:55:160
  21. abi = [{"constant":false,"inputs":[{"name":"n","type":"string"}],"name":"set","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]
  22. ====> cns history list index = 3 <====
  23. contract = HelloWorld
  24. version =
  25. address = 0x269ab4bc23b07efeb3c3fd52eecfc4cbe6a50859
  26. timestamp = 1516866720115 => 2018/1/25 15:52:0:115
  27. abi = [{"constant":false,"inputs":[{"name":"n","type":"string"}],"name":"set","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]
  28.  
  29. 然后查看需要被找回的合约是哪个。
  30. babel-node cns_manager.js reset HelloWorld 3
  31. cns_manager.js ........................Begin........................
  32. ====> Are you sure update the cns of the contract ?(Y/N)
  33. Y
  34. cns update operation => cns_name = HelloWorld
  35. cns_name =>HelloWorld
  36. contract =>HelloWorld
  37. version =>
  38. address =>0x269ab4bc23b07efeb3c3fd52eecfc4cbe6a50859
  39. abi =>[{"constant":false,"inputs":[{"name":"n","type":"string"}],"name":"set","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]
  40. 发送交易成功: 0x4809a6105916a483ca70c4efe8e306bc01ca5d937515320d09e94a83f4a91b76
  41.  
  42. 此时再调用HelloWorldget接口:
  43. curl -X POST --data '{"jsonrpc":"2.0","method":"eth_call","params":[{"data":{"contract":"HelloWorld","version":"","func":"get","params":[]}},"latest"],"id":1}' "http://127.0.0.1:8746"
  44. {"id":1,"jsonrpc":"2.0","result":"[\"call defaut version\"]\n"}
  45.  
  46. 返回为call defaut version, 说明当前CNS调用的合约已经是最后一次比覆盖的合约。
  • js调用
  1. //调用HelloWorld get接口
  2. var result = web3sync.callByNameService("HelloWorld","get","",[]);
  3.  
  4. //调用HelloWorld v-1.0 get接口
  5. var result = web3sync.callByNameService("HelloWorld","get","v-1.0",[]);
  6.  
  7. //调用HelloWorld set接口 sendRawTransaction
  8. var result = web3sync.sendRawTransactionByNameService(config.account,config.privKey,"HelloWorld","set","",["test message!"]);
  9.  
  10. //调用HelloWorld v-1.0 set接口 sendRawTransaction
  11. var result = web3sync.sendRawTransactionByNameService(config.account,config.privKey,"HelloWorld","set","v-1.0",["test message!"]);

附录一. 重载函数的调用

solidity支持函数重载, 当solidity中存在重载函数时, 使用CNS调用的参数跟之前有所不同:

  1. //file : OverloadTest.sol
  2. pragma solidity ^0.4.4;
  3. contract OverloadTest {
  4. string public msg;
  5. uint256 public u;
  6.  
  7. function OverloadTest() {
  8. msg = "OverloadTest Test";
  9. u = 0x01;
  10. }
  11.  
  12. function set(string _msg) public {
  13. msg = _msg;
  14. }
  15. function set(uint256 _u) public {
  16. u = _u;
  17. }
  18.  
  19. function get() public constant returns(string){
  20. return msg;
  21. }
  22.  
  23. function get(uint256 i) public constant returns(uint256){
  24. return u;
  25. }
  26. }

在OverloadTest.sol合约中:set函数是一个重载函数, 一个函数原型为set(string),另一个为set(uint256).get函数也是一个重载函数, 一个函数原型为get(), 另一个为get(uint256).

部署合约:

  1. babel-node deploy.js OverloadTest
  2. RPC=http://0.0.0.0:8546
  3. Ouputpath=./output/
  4. deploy.js ........................Start........................
  5. OverloadTest编译成功!
  6. 发送交易成功: 0xff8a5708b3f7b335570a50639f2073e5e0b8b2002faa909dc75727059de94f4e
  7. OverloadTest合约地址 0x919868496524eedc26dbb81915fa1547a20f8998
  8. OverloadTest部署成功!
  9. cns add operation => cns_name = OverloadTest
  10. cns_name =>OverloadTest
  11. contract =>OverloadTest
  12. version =>
  13. address =>0x919868496524eedc26dbb81915fa1547a20f8998
  14. abi =>[{"constant":false,"inputs":[{"name":"_msg","type":"string"}],"name":"set","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_u","type":"uint256"}],"name":"set","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"msg","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"i","type":"uint256"}],"name":"get","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"u","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"inputs":[],"payable":false,"type":"constructor"}]
  15. ===>> namecall params = {"contract":"ContractAbiMgr","func":"addAbi","version":"","params":["OverloadTest","OverloadTest","","[{\"constant\":false,\"inputs\":[{\"name\":\"_msg\",\"type\":\"string\"}],\"name\":\"set\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_u\",\"type\":\"uint256\"}],\"name\":\"set\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"msg\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"get\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"i\",\"type\":\"uint256\"}],\"name\":\"get\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"u\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"inputs\":[],\"payable\":false,\"type\":\"constructor\"}]","0x919868496524eedc26dbb81915fa1547a20f8998"]}
  16. 发送交易成功: 0x56e2267cd46fddc11abc4f38d605adc1f76d3061b96cf4026b09ace3502d2979
对于重载函数, 在使用CNS方式调用时, func参数需要指定完整的函数原型, 不能仅仅只指定函数的名称

调用get()时, “func”:”get()”调用get(uint256 i)时, “func”:”get(uint256)”调用set(string _msg)时, “func”:”set(string)”调用set(uint256 _u)时, “func”:”set(uint256)”

下面是调用的示例:

  1. 调用get()接口:
  2. curl -X POST --data '{"jsonrpc":"2.0","method":"eth_call","params":[{"data":{"contract":"OverloadTest","version":"","func":"get","params":[]}},"latest"],"id":1}' "http://127.0.0.1:8546"
  3. {"id":1,"jsonrpc":"2.0","result":"[\"OverloadTest Test\"]\n"}
  4.  
  5. 调用get(uint256 i)接口:
  6. curl -X POST --data '{"jsonrpc":"2.0","method":"eth_call","params":[{"data":{"contract":"OverloadTest","version":"","func":"get(uint256)","params":[1]}},"latest"],"id":1}' "http://127.0.0.1:8546"
  7. {"id":1,"jsonrpc":"2.0","result":"[\"1\"]\n"}
  8.  
  9. 使用js调用set(string _msg):
  10. var result = web3sync.sendRawTransactionByNameService(config.account,config.privKey,"OverloadTest","set(string)","",["test message!"]);
  11.  
  12. 使用js调用set(uint256 _i)):
  13. var result = web3sync.sendRawTransactionByNameService(config.account,config.privKey,"OverloadTest","set(uint256)","",["0x111"]);

附录二. java客户端的使用.

我们还是以上述HelloWorld.sol合约为例,提供一个完整的例子。

  • 参考上面的流程, 部署HelloWorld.sol合约, 使用cns_manager.js工具注册HelloWorld合约信息到CNS管理合约。
  • 下载web3sdk, 版本号需要>=V1.1.0, web3sdk的使用以及下载:https://github.com/FISCO-BCOS/web3sdk
  • 使用web3sdk生成HelloWorld的java wrap代码参考教程。在示例中使用HelloWorld.sol生成的java代码的包名为:org.bcos.cns, 生成的代码为:
  1. package org.bcos.cns;
  2.  
  3. import java.math.BigInteger;
  4. import java.util.Arrays;
  5. import java.util.Collections;
  6. import java.util.concurrent.Future;
  7. import org.bcos.channel.client.TransactionSucCallback;
  8. import org.bcos.web3j.abi.TypeReference;
  9. import org.bcos.web3j.abi.datatypes.Function;
  10. import org.bcos.web3j.abi.datatypes.Type;
  11. import org.bcos.web3j.abi.datatypes.Utf8String;
  12. import org.bcos.web3j.crypto.Credentials;
  13. import org.bcos.web3j.protocol.Web3j;
  14. import org.bcos.web3j.protocol.core.methods.response.TransactionReceipt;
  15. import org.bcos.web3j.tx.Contract;
  16. import org.bcos.web3j.tx.TransactionManager;
  17.  
  18. /**
  19. * Auto generated code.<br>
  20. * <strong>Do not modify!</strong><br>
  21. * Please use the <a href="https://docs.web3j.io/command_line.html">web3j command line tools</a>, or {@link org.bcos.web3j.codegen.SolidityFunctionWrapperGenerator} to update.
  22. *
  23. * <p>Generated with web3j version none.
  24. */
  25. public final class HelloWorld extends Contract {
  26. private static final String BINARY = "6060604052341561000c57fe5b5b604060405190810160405280600b81526020017f48692c57656c636f6d652100000000000000000000000000000000000000000081525060009080519060200190610059929190610060565b505b610105565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106100a157805160ff19168380011785556100cf565b828001600101855582156100cf579182015b828111156100ce5782518255916020019190600101906100b3565b5b5090506100dc91906100e0565b5090565b61010291905b808211156100fe5760008160009055506001016100e6565b5090565b90565b6102e2806101146000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680634ed3885e146100465780636d4ce63c146100a0575bfe5b341561004e57fe5b61009e600480803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050610139565b005b34156100a857fe5b6100b0610154565b60405180806020018281038252838181518152602001915080519060200190808383600083146100ff575b8051825260208311156100ff576020820191506020810190506020830392506100db565b505050905090810190601f16801561012b5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b806000908051906020019061014f9291906101fd565b505b50565b61015c61027d565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156101f25780601f106101c7576101008083540402835291602001916101f2565b820191906000526020600020905b8154815290600101906020018083116101d557829003601f168201915b505050505090505b90565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061023e57805160ff191683800117855561026c565b8280016001018555821561026c579182015b8281111561026b578251825591602001919060010190610250565b5b5090506102799190610291565b5090565b602060405190810160405280600081525090565b6102b391905b808211156102af576000816000905550600101610297565b5090565b905600a165627a7a723058205f78cf9b4365c5a429ff9e4ebc4abf1f9e9d44f0a41c19c85c9d394438f3fe7b0029";
  27.  
  28. public static final String ABI = "[{\"constant\":false,\"inputs\":[{\"name\":\"n\",\"type\":\"string\"}],\"name\":\"set\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"get\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"type\":\"function\"},{\"inputs\":[],\"payable\":false,\"type\":\"constructor\"}]";
  29.  
  30. private HelloWorld(String contractAddress, Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit, Boolean isInitByName) {
  31. super(BINARY, contractAddress, web3j, credentials, gasPrice, gasLimit, isInitByName);
  32. }
  33.  
  34. private HelloWorld(String contractAddress, Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit, Boolean isInitByName) {
  35. super(BINARY, contractAddress, web3j, transactionManager, gasPrice, gasLimit, isInitByName);
  36. }
  37.  
  38. private HelloWorld(String contractAddress, Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) {
  39. super(BINARY, contractAddress, web3j, credentials, gasPrice, gasLimit, false);
  40. }
  41.  
  42. private HelloWorld(String contractAddress, Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) {
  43. super(BINARY, contractAddress, web3j, transactionManager, gasPrice, gasLimit, false);
  44. }
  45.  
  46. public Future<TransactionReceipt> set(Utf8String n) {
  47. Function function = new Function("set", Arrays.<Type>asList(n), Collections.<TypeReference<?>>emptyList());
  48. return executeTransactionAsync(function);
  49. }
  50.  
  51. public void set(Utf8String n, TransactionSucCallback callback) {
  52. Function function = new Function("set", Arrays.<Type>asList(n), Collections.<TypeReference<?>>emptyList());
  53. executeTransactionAsync(function, callback);
  54. }
  55.  
  56. public Future<Utf8String> get() {
  57. Function function = new Function("get",
  58. Arrays.<Type>asList(),
  59. Arrays.<TypeReference<?>>asList(new TypeReference<Utf8String>() {}));
  60. return executeCallSingleValueReturnAsync(function);
  61. }
  62.  
  63. public static Future<HelloWorld> deploy(Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit, BigInteger initialWeiValue) {
  64. return deployAsync(HelloWorld.class, web3j, credentials, gasPrice, gasLimit, BINARY, "", initialWeiValue);
  65. }
  66.  
  67. public static Future<HelloWorld> deploy(Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit, BigInteger initialWeiValue) {
  68. return deployAsync(HelloWorld.class, web3j, transactionManager, gasPrice, gasLimit, BINARY, "", initialWeiValue);
  69. }
  70.  
  71. public static HelloWorld load(String contractAddress, Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) {
  72. return new HelloWorld(contractAddress, web3j, credentials, gasPrice, gasLimit, false);
  73. }
  74.  
  75. public static HelloWorld load(String contractAddress, Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) {
  76. return new HelloWorld(contractAddress, web3j, transactionManager, gasPrice, gasLimit, false);
  77. }
  78.  
  79. public static HelloWorld loadByName(String contractName, Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) {
  80. return new HelloWorld(contractName, web3j, credentials, gasPrice, gasLimit, true);
  81. }
  82.  
  83. public static HelloWorld loadByName(String contractName, Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) {
  84. return new HelloWorld(contractName, web3j, transactionManager, gasPrice, gasLimit, true);
  85. }
  86. }

在生成的代码中多了两个loadByName函数。

  • 函数调用
  1. package org.bcos.main;
  2.  
  3. import java.math.BigInteger;
  4. import java.util.concurrent.Future;
  5.  
  6. import org.bcos.channel.client.Service;
  7. import org.bcos.cns.HelloWorld;
  8. import org.bcos.web3j.abi.datatypes.Utf8String;
  9. import org.bcos.web3j.crypto.Credentials;
  10. import org.bcos.web3j.crypto.ECKeyPair;
  11. import org.bcos.web3j.crypto.Keys;
  12. import org.bcos.web3j.protocol.Web3j;
  13. import org.bcos.web3j.protocol.channel.ChannelEthereumService;
  14. import org.bcos.web3j.protocol.core.methods.response.TransactionReceipt;
  15. import org.springframework.context.ApplicationContext;
  16. import org.springframework.context.support.ClassPathXmlApplicationContext;
  17.  
  18. public class Main {
  19. public static void main(String[] args) throws Exception {
  20.  
  21. ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
  22. //init service
  23. Service service = context.getBean(Service.class);
  24. ChannelEthereumService channelEthereumService = new ChannelEthereumService();
  25. channelEthereumService.setTimeout(10000);
  26. channelEthereumService.setChannelService(service);
  27.  
  28. //init web3
  29. Web3j web3j = Web3j.build(channelEthereumService);
  30. service.run();
  31.  
  32. //初始化交易签名私钥
  33. ECKeyPair keyPair = Keys.createEcKeyPair();
  34. Credentials credentials = Credentials.create(keyPair);
  35.  
  36. BigInteger gasPrice = new BigInteger("99999999");
  37. BigInteger gasLimit = new BigInteger("99999999");
  38.  
  39. //通过loadByName方式构建合约对象时, 后面通过合约对象调用合约接口时, 会以CNS的方式调用
  40. HelloWorld instance = HelloWorld.loadByName("HelloWorld", web3j, credentials, gasPrice , gasLimit);
  41.  
  42. //调用HelloWorld set接口
  43. Future<TransactionReceipt> receiptResult = instance.set(new Utf8String("HelloWorld Test."));
  44. receiptResult.get();
  45.  
  46. //调用HelloWorld get接口
  47. Future<Utf8String> result = instance.get();
  48. System.out.println("HelloWorld get result = " + result.get().toString());
  49.  
  50.  
  51. return;
  52. }
  53. }

通过loadByName方式构建合约对象时, 后面通过合约对象调用合约接口时, 会以CNS的方式调用。

HelloWorld instance = HelloWorld.loadByName(“HelloWorld”, web3j, credentials, gasPrice , gasLimit);

HelloWorld的合约对象通过loadByName方式构建, 所以后续的get跟set的调用都是以CNS方式进行调用的。

  • 说明:对于合约XX.sol生成的java Wrap代码中的loadByName原型如下:
  1. public static XX loadByName(String contractName, Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) {
  2. return new XX(contractName, web3j, credentials, gasPrice, gasLimit, true);
  3. }
  4.  
  5. public static XX loadByName(String contractName, Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) {
  6. return new XX(contractName, web3j, transactionManager, gasPrice, gasLimit, true);
  7. }

其中, contractname参数格式为:合约的名称@合约版本号, 如果合约没有版本号, 则为合约的名称。

  • 总结使用java客户端调用CNS的步骤为:a. 使用JS工具部署合约.b. 使用cns_nameger.js工具注册合约信息到CNS管理合约.c. 使用websdk工具生成合约的java Wrap代码.d. 将生成的代码加入自己的工程, 通过loadByName接口构造合约对象.e. 调用合约接口.