Skip to content

Commit

Permalink
add fabric cross chain readme
Browse files Browse the repository at this point in the history
  • Loading branch information
chenxifun committed Apr 28, 2021
1 parent 2ab4796 commit b42277a
Show file tree
Hide file tree
Showing 7 changed files with 265 additions and 24 deletions.
6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

67 changes: 44 additions & 23 deletions sample/irita/consumers/fabric/Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,35 +17,43 @@ import (
"github.com/BSNDA/ICH/sample/irita/consumers/fabric/crosschaincode"
)
```
在invoke方法中直接调用 `crosschaincode.CallService`方法,该方法的参数如下:
在invoke方法中直接调用 `crosschaincode.CallCrossService`方法,该方法的参数如下:
* stub : shim.ChaincodeStubInterface
* serviceName : 调用的跨链服务名称,ETH的为 `nft` ,FISCO BCOS 为`bcos-store`
* serviceName : 调用的跨链服务名称,通用联盟链跨链为 `cross_service`
* crossChaincode : 跨链管理合约地址
* input : 跨链服务的输入对象,
* callbackCC :回调的合约名称
* callbackFcn :回调的合约方法名称
* timeout : 超时时间

其中 input 参数根据跨链服务不同,传入的类型不同
在ETH 的服务中`input`结构如下
```
type Input struct {
ABIEncoded string `json:"abi_encoded,omitempty"`
To string `json:"to"`
AmountToMint string `json:"amount_to_mint"`
MetaID string `json:"meta_id"`
SetPrice string `json:"set_price"`
IsForSale bool `json:"is_for_sale"`
其中 input 参数根据跨链调用的目标链类型不同,传入的类型不同
在以`fabric`为目标链的服务中`input`结构为`crosschaincode.FabricInput`
```
type FabricInput struct {
ChainId uint64 `json:"chainId"`
ChainCode string `json:"chainCode"`
FunType string `json:"funType"`
Args []string `json:"args"`
}
```
> `nft`服务为部署在ETH Ropsten测试网的MintBase合约,合约地址为:`0x80f2a29e861a1888603b6bbd54453ee995c808ad`
> `ChainId` 为调用的目标链的链ID,可以在BSN的应用详情页找到
> `ChainCode` 为调用的目标链的合约名称
> `FunType` 为调用的目标链的方法类型,可选为`query``invoke`
> `Args` 为调用的目标链的参数,其中第一个参数为调用的合约方法名,其他为参数
在FISCO BCOS 服务中`input`结构如下
在以`FISCO-BCOS`为目标链的服务中`input`结构为`crosschaincode.FiscoBcosInput`
```
type BcosInput struct {
Value string `json:"value"`
type FiscoBcosInput struct {
OptType string `json:"optType"`
ChainId uint64 `json:"chainId"`
ContractAddress string `json:"contractAddress"`
CallData string `json:"callData"`
}
```
> `bcos-store`服务为部署在BSN测试网的 fisco-bcos 合约,参考[Store.sol](https://github.com/BSNDA/ICH/blob/main/sample/irita/services/fiscobcos/store/Store.sol),合约地址为:`0xc5a44ba642f4609e51a96d04d211b86f094a4051`
> `OptType`为调用的合约的方法类型,其中`constant``call`,`非constant``tx`
> `ChainId` 为调用的目标链的链ID,可以在BSN的应用详情页找到
> `ContractAddress` 为调用的目标合约的合约地址
> `CallData` 为使用调用的目标合约的合约ABI、合约方法名、合约参数、序列化后的数据的哈希字符串,不包含`0x`,可以参考BSN网关go语言SDK的 [ParesData](https://github.com/BSNDA/PCNGateway-Go-SDK/blob/6d97d885f96597f4b35040df17fdca1fbcda07ab/pkg/core/trans/fiscobcos/trans.go#L24)
调用成功,将返回唯一的请求ID,请注意保存该值,在回调方法中可以根据该值判断跨链结果。

Expand All @@ -71,18 +79,31 @@ type InputData struct {
```
其中 `Body` 为对应服务的输出对象,

在ETH 的服务中`output`结构如下
在以`Fabric`为目标链的调用中 `output`结构如下
```
type Output struct {
NftID string `json:"nft_id"`
type FabricOutput struct {
TxValidationCode int32 `json:"txValidationCode"`
ChaincodeStatus int32 `json:"chaincodeStatus"`
TxId string `json:"txId"`
Payload string `json:"payload"`
}
```
在FISCO BCOS 服务中`output`结构如下
> `TxValidationCode` 为交易的状态
> `ChaincodeStatus`为合约的执行状态
> `TxId`为交易ID
> `Payload` 为合约的返回结果的哈希字符串
在以`FISCO-BCOS`为目标链的调用中`output`结构如下
```
type BcosOutput struct {
Key string `json:"key"`
type FiscoBcosOutput struct {
Result string `json:"result,omitempty"`
Status bool `json:"status,omitempty"`
TxHash string `json:"tx_hash,omitempty"`
}
```
> `Result` 为合约的返回结果,调用的方法为`call`时,有值
> `Status` 合约的执行结果状态
> `TxHash` 为合约的执行交易哈希,调用的方法为`tx`时,有值
4. 链码打包
由于该链码引用了外部的包,需要在打包时将外部包同时打包,可以使用`govendor`打包
Expand Down
46 changes: 46 additions & 0 deletions sample/irita/consumers/fabric/chaincode/chaincode.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ func (c *SCChaincode) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
return c.callFiscoStore(stub, args)
}

if strings.ToLower(function) == "callchainlink" {
return c.callChainlink(stub, args)
}

if strings.ToLower(function) == "callback" {
return c.callback(stub, args)
}
Expand All @@ -50,9 +54,51 @@ func (c *SCChaincode) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
return c.query(stub, args)
}

if strings.ToLower(function) == "callfabric" {
return c.callFabric(stub, args)
}

if strings.ToLower(function) == "callfisco" {
return c.callFisco(stub, args)
}

return shim.Error("function not found")
}

func (c *SCChaincode) callChainlink(stub shim.ChaincodeStubInterface, args []string) peer.Response {
if len(args) == 0 {
return shim.Error("the args cannot be empty")
}
serviceName := args[0]
cb_cc := ""
cb_fcn := "callback"

if len(args) >= 2 {
cb_cc = args[1]
}
if len(args) >= 3 {
cb_fcn = args[2]
}
fmt.Println(cb_cc, cb_fcn)
reqId, err := crosschaincode.CallService(stub, serviceName, new(struct{}), cb_cc, cb_fcn, 100)
if err != nil {
return shim.Error("Chainlink has failed ," + err.Error())
}
fmt.Println(reqId)

cd := &CrossData{
Id: reqId,
Input: args[0],
}

cdb, _ := json.Marshal(cd)
if err := stub.PutState(crossKey(reqId), cdb); err != nil {
return shim.Error(fmt.Sprintf("put data info error;%s", err))
}

return shim.Success([]byte(reqId))
}

func (c *SCChaincode) callNFT(stub shim.ChaincodeStubInterface, args []string) peer.Response {
serviceName := "nft"

Expand Down
101 changes: 101 additions & 0 deletions sample/irita/consumers/fabric/chaincode/chaincode_ex.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package chaincode

import (
"encoding/json"
"fmt"
"github.com/BSNDA/ICH/sample/irita/consumers/fabric/crosschaincode"
"github.com/hyperledger/fabric/core/chaincode/shim"
"github.com/hyperledger/fabric/protos/peer"
"strconv"
)

func (c *SCChaincode) callFabric(stub shim.ChaincodeStubInterface, args []string) peer.Response {

if len(args) < 7 {
return shim.Error("Length cannot be less than 7")
}

serviceName := args[0]
crossChaincode := args[1]
cb_cc := args[2]
chainIdStr := args[3]

chainId, err := strconv.ParseUint(chainIdStr, 10, 64)
if err != nil {
return shim.Error("Incorrect chainID type")
}

chainCode := args[4]
funType := args[5]
args = args[6:]

input := &crosschaincode.FabricInput{
ChainId: chainId,
ChainCode: chainCode,
FunType: funType,
Args: args,
}

cb_fcn := "callback"

reqId, err := crosschaincode.CallCrossService(stub, serviceName, crossChaincode, input, cb_cc, cb_fcn, 100)
if err != nil {
return shim.Error("callFabric has failed ," + err.Error())
}
fmt.Println(reqId)

jsonBytes, _ := json.Marshal(input)
cd := &CrossData{
Id: reqId,
Input: string(jsonBytes),
}

cdb, _ := json.Marshal(cd)
if err := stub.PutState(crossKey(reqId), cdb); err != nil {
return shim.Error(fmt.Sprintf("put data info error;%s", err))
}

return shim.Success([]byte(reqId))
}

type CallData struct {
ServiceName string `json:"service_name"`
CrossChainCode string `json:"cc_code"`
CallBackChaincode string `json:"cb_cc"`
CrossData *crosschaincode.FiscoBcosInput `json:"cross_data"`
}

func (c *SCChaincode) callFisco(stub shim.ChaincodeStubInterface, args []string) peer.Response {

if len(args) < 1 {
return shim.Error("Length cannot be less than 1")
}

callData := &CallData{}
fmt.Println(args[0])

err := json.Unmarshal([]byte(args[0]), callData)
if err != nil {
return shim.Error("args format failed ," + err.Error())
}

cb_fcn := "callback"

reqId, err := crosschaincode.CallCrossService(stub, callData.ServiceName, callData.CrossChainCode, callData.CrossData, callData.CallBackChaincode, cb_fcn, 100)
if err != nil {
return shim.Error("callFabric has failed ," + err.Error())
}
fmt.Println(reqId)

cd := &CrossData{
Id: reqId,
Input: args[0],
}

cdb, _ := json.Marshal(cd)
if err := stub.PutState(crossKey(reqId), cdb); err != nil {
return shim.Error(fmt.Sprintf("put data info error;%s", err))
}

return shim.Success([]byte(reqId))
}
40 changes: 39 additions & 1 deletion sample/irita/consumers/fabric/crosschaincode/crosschaincode.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import (
)

const (
CrossChaincode = "cc_cross"
CrossChaincode = "cc_cross"
CrossServiceName = "cross_service"
)

func CallService(stub shim.ChaincodeStubInterface, serviceName string, input interface{}, callbackCC, callbackFcn string, timeout uint64) (string, error) {
Expand Down Expand Up @@ -45,6 +46,43 @@ func CallService(stub shim.ChaincodeStubInterface, serviceName string, input int
return txId, nil
}

func CallCrossService(stub shim.ChaincodeStubInterface, serviceName string, crossChaincode string, input interface{}, callbackCC, callbackFcn string, timeout uint64) (string, error) {

data := &InputData{
Header: struct{}{},
Body: input,
}
dataBytes, _ := json.Marshal(data)

req := &ServiceRequest{
ServiceName: serviceName,
Input: string(dataBytes),
Timeout: timeout,
}

fmt.Printf("callbackCC:%s ,callbackFcn:%s", callbackCC, callbackFcn)
if strings.TrimSpace(callbackCC) != "" && strings.TrimSpace(callbackFcn) != "" {
req.CallBack = &CallBackInfo{
ChainCode: callbackCC,
FuncName: callbackFcn,
}
}

b, _ := json.Marshal(req)

var args [][]byte
args = append(args, []byte("callservice"))
args = append(args, b)

res := stub.InvokeChaincode(crossChaincode, args, "")
fmt.Println("res.Status:", res.Status)
fmt.Println("res.Message:", res.Message)
txId := string(res.Payload)
fmt.Println("res.Payload:", txId)
//stub.SetEvent(fmt.Sprintf("CallService_%s", txId), []byte(txId))
return txId, nil
}

func GetCallBackInfo(output string) (*ServiceResponse, error) {
fmt.Println("output:", output)
res := &ServiceResponse{}
Expand Down
29 changes: 29 additions & 0 deletions sample/irita/consumers/fabric/crosschaincode/entity.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,32 @@ type ServiceResponse struct {
Output string `json:"output,omitempty"`
IcRequestId string `json:"icRequestID,omitempty"`
}

//bsn IRITA跨链中,标链为 fabric的标准输入结构
type FabricInput struct {
ChainId uint64 `json:"chainId"`
ChainCode string `json:"chainCode"`
FunType string `json:"funType"`
Args []string `json:"args"`
}

//bsn IRITA跨链中,目标链为 FISCO-BCOS的标准输入结构
type FiscoBcosInput struct {
OptType string `json:"optType"`
ChainID uint64 `json:"chainId"`
ContractAddress string `json:"contractAddress"`
CallData string `json:"callData"`
}

type FabricOutput struct {
TxValidationCode int32 `json:"txValidationCode"`
ChaincodeStatus int32 `json:"chaincodeStatus"`
TxId string `json:"txId"`
Payload string `json:"payload"`
}

type FiscoBcosOutput struct {
Result string `json:"result,omitempty"`
Status bool `json:"status,omitempty"`
TxHash string `json:"tx_hash,omitempty"`
}
Binary file modified sample/irita/consumers/fabric/fabric.zip
Binary file not shown.

0 comments on commit b42277a

Please sign in to comment.