合约跨链调用(Interchain)
优质
小牛编辑
128浏览
2023-12-01
WeCross支持由合约发起跨链调用,即可在源链的智能合约中发起对其它链资源的调用。
原理解析
WeCross提供了两个系统合约,分别是代理合约和桥接合约。代理合约是WeCross调用链上其它合约的统一入口,桥接合约则负责保存链上的跨链调用请求。跨链路由通过轮询桥接合约的任务队列,获取跨链调用请求,然后完成请求的转发和处理。
合约跨链调用具体流程如下:
- 源链的业务合约调用源链的桥接合约,注册跨链请求以及回调接口
- 源链的跨链路由轮询源链的桥接合约,获取跨链请求
- 源链的跨链路由解析跨链请求,完成对目标链的调用
- 源链的跨链路由将目标链返回的结果作为参数,调用源链的回调接口
- 源链的跨链路由将回调接口的调用结果注册到源链的桥接合约
桥接合约
WeCross提供了Solidity版本和Golang版本的桥接合约。
Solidity版本合约下载地址: GitHub访问链接 Gitee访问链接
Golang版本合约下载地址: GitHub访问链接 Gitee访问链接
- Solidity版本
/** 供业务合约调用,注册跨链调用请求 * * @param _path 目标链合约的路径 * @param _method 调用方法名 * @param _args 调用参数列表 * @param _callbackPath 回调的合约路径 * @param _callbackMethod 回调方法名 * @return 跨链请求的唯一ID */ function interchainInvoke( string memory _path, string memory _method, string[] memory _args, string memory _callbackPath, string memory _callbackMethod ) public returns(string memory uid) /** 供用户调用,查询回调的调用结果 * * @param _uid 跨链请求的唯一ID * @return 字符串数组: [事务ID, 事务Seq, 错误码, 错误消息, 回调调用结果的JSON序列化] */ function selectCallbackResult( string memory _uid ) public view returns(string[] memory)
- Golang版本
/** * @param [path, method, args, callbackPath, callbackMethod] * @return 跨链请求的唯一ID */ func (h *Hub) interchainInvoke( stub shim.ChaincodeStubInterface, args []string ) peer.Response /** * @param uid * @return 字符串数组: [事务ID, 事务Seq, 错误码, 错误消息, 回调调用结果的JSON序列化] */ func (h *Hub) selectCallbackResult( stub shim.ChaincodeStubInterface, args []string ) peer.Response
重要
- 调用的目标链的接口定义必须匹配:
string[] func(string[] args)
- 回调函数的接口定义必须匹配:
string[] func(bool state, string[] result)
,state表示调用目标链是否成功,result是调用结果 - 实现跨链调用的业务合约编写规范可参考示例合约,GitHub访问: Solidity版 和 Golang版
- Gitee访问: Solidity版 和 Golang版
操作示例
WeCross控制台提供了两种语言版本的跨链调用示例合约,示例合约的接口包括:
init(): 传入本链的桥接合约地址进行初始化 interchain(): 跨链调用的发起接口,其内部调用了桥接合约的interchainInvoke接口 get(): 获取data set(): 更新data callback(): 使用跨链调用的结果更新data
两个示例合约联动过程:A链的示例合约发起一个跨链调用,调用B链的示例合约的set接口,更新B链的data,然后触发回调,调用A链的callback接口并更新A链的data。
通过上述方式,一次控制台调用就能完成两条链数据的更新。
前期准备
以下操作示例涉及 FISCO BCOS 和 Hyperledger Fabric 两条链,整个跨链网络的搭建和部署请参考快速入门章节。
部署跨链调用示例合约
完成环境搭建后,在WeCross控制台执行以下命令:
# 登录 [WeCross]> login org1-admin 123456 # 部署BCOS链示例合约 [WeCross.org1-admin]> bcosDeploy payment.bcos.interchain contracts/solidity/InterchainSample.sol InterchainSample 1.0 Result: 0xb9fe7fd54b0c595c40a6417ba35908f310c0aee9 # 切换Fabric默认账户 [WeCross.org1-admin]> setDefaultAccount Fabric1.4 2 # 安装链码 [WeCross.org1-admin]> fabricInstall payment.fabric.interchain Org2 contracts/chaincode/interchain 1.0 GO_LANG path: classpath:contracts/chaincode/interchain Result: Success # 切换Fabric默认账户 [WeCross.org1-admin]> setDefaultAccount Fabric1.4 1 # 安装链码 [WeCross.org1-admin]> fabricInstall payment.fabric.interchain Org1 contracts/chaincode/interchain 1.0 GO_LANG path: classpath:contracts/chaincode/interchain Result: Success # 实例化链码 [WeCross.org1-admin]> fabricInstantiate payment.fabric.interchain ["Org1","Org2"] contracts/chaincode/interchain 1.0 GO_LANG default [] # 等待实例化完成
查询桥接合约地址
因为示例合约需要调用桥接合约,所以先查询桥接合约地址,以用于初始化示例合约。
在BCOS链的跨链路由根目录下执行命令:
# 进入跨链路由根目录 cd ~/wecross-demo/routers-payment/127.0.0.1-8250-25500 # 非国密链,其中chains/bcos是链的路径,在conf目录下可查看 java -cp 'conf/:lib/*:plugin/*' com.webank.wecross.stub.bcos.normal.preparation.HubContractDeployment getAddress chains/bcos # 国密链 java -cp 'conf/:lib/*:plugin/*' com.webank.wecross.stub.bcos.guomi.preparation.HubContractDeployment getAddress chains/bcos # 结果 WeCrossHub address: 0xb00b0a913f2c4b6bc9e7a588061a8bc55d07afe1
因此BCOS链的桥接合约地址为0xb00b0a913f2c4b6bc9e7a588061a8bc55d07afe1
。而Fabric链的桥接合约名字固定为WeCrossHub
,无需查询。
初始化示例合约
在WeCross控制台执行以下命令:
# 登录 [WeCross]> login org1-admin 123456 # 初始化BCOS示例合约 [WeCross.org1-admin]> sendTransaction payment.bcos.interchain init 0xb00b0a913f2c4b6bc9e7a588061a8bc55d07afe1 # 初始化Fabric示例合约,其中mychannel是channel名 [WeCross.org1-admin]> sendTransaction payment.fabric.interchain init mychannel WeCrossHub
发起跨链调用
在WeCross控制台执行以下命令:
# 登录 [WeCross]> login org1-admin 123456 # 查看示例合约原始状态 [WeCross.org1-admin]> call payment.fabric.interchain get Result: [["Talk is cheap, show me the code."]] [WeCross.org1-admin]> call payment.bcos.interchain get Result: [["Talk is cheap, show me the code."]] # BCOS发起跨链调用 # 命令解析: 调用示例合约的interchain接口,该接口触发跨链调用 # 参数列表: [目标链资源路径] [目标接口] [调用参数] [回调资源路径] [回调接口] [WeCross.org1-admin]> sendTransaction payment.bcos.interchain interchain payment.fabric.interchain set "Hello world" payment.bcos.interchain callback Txhash : 0x840355ed53de047594f0a5778312edb2f2fe93908eb5efb6336cf535805f5c26 BlockNum: 1898 Result : [1] # 查询示例合约,发现两条链的数据都发生了变化 [WeCross.org1-admin]> call payment.bcos.interchain get Result: [["Hello world"]] [WeCross.org1-admin]> call payment.fabric.interchain get Result: [["Hello world"]] # 根据跨链调用的返回值,查询调用回调的结果 [WeCross.org1-admin]> call payment.bcos.WeCrossHub selectCallbackResult 1 Result: [[ "0", "0", "0", "Success", "[\"Hello world\"]" ]] # Fabric发起跨链调用 [WeCross.org1-admin]> sendTransaction payment.fabric.interchain interchain payment.bcos.interchain set "Hello WeCross" payment.fabric.interchain callback Txhash : cf7eda25f1c0515b68d702ed495fdbbefed6bdcfd4a3bc68aaab315631d3d102 BlockNum: 2386 Result : [1] # 查询示例合约 [WeCross.org1-admin]> call payment.bcos.interchain get Result: [[ "Hello WeCross" ]] [WeCross.org1-admin]> call payment.fabric.interchain get Result: [[ "Hello WeCross" ]] # 查询调用回调的结果 [WeCross.org1-admin]> call payment.fabric.WeCrossHub selectCallbackResult 1 Result: [["0","0","0","Success","[ \"Hello WeCross\" ]"]]