陆羽协议3-同构跨链

第三步-同构跨链:2台机器部署2个fabirc网络,实现从一个合约调用另一个合约

实现的结构图如下及调用路径如下:

合约实现跨链调用,不管是同构还是异构,都是通过合约中的event事件。调用特定的event,本地router捕捉到event,发送给对应的远程router。

对于fabric同构跨链来说,由于其特殊的账户证书机制,同一个账户在不同的fabric链上也不能通用。在实际的使用中,可以通过使用同一个账户的公钥,用两条链的ca颁发,生成两个证书,注册到对应的account manager中,实现一个账户在两个router中通用的效果。还有一种方法是在一个链中生成crypto-config,直接拷贝到另一个链上去用,先使用这种方法。

理论上说可以替换掉router及account配置文件,删除原有一级账户,使用新的配置文件后重新生成一级账户,为了避免因为配置问题导致的不明错误,加快测试进度,将两台服务器都从头开始。

101删除重启,按照前阶段步骤配置。
102删除,把101的crypto-config配置拷贝过来

scp -r root@192.168.92.101:/root/go/src/github.com/hyperledger/fabric-samples/first-network/crypto-config/ /root/go/src/github.com/hyperledger/fabric-samples/first-network/crypto-config
cd /root/go/src/github.com/hyperledger/fabric-samples/first-network

修改byfn脚本,把replacePrivateKeygenerateChannelArtifacts单独拎出来执行(因为crypto-config已经存在,所以replacePrivateKeygenerateChannelArtifacts就不再执行)

 if [ ! -d "crypto-config" ]; then
        generateCerts
        replacePrivateKey
        generateChannelArtifacts
      fi

启动fabric网络

sh byfn.sh up -i 1.4.4

使用新的fabric插件

git clone https://github.com/WeBankBlockchain/WeCross-Fabric1-Stub.git
cd WeCross-Fabric1-Stub/
git checkout feature-luyu-call-from-chain
gradle assemble

替换掉原本的plugin包,看是否还正常

mv /root/java/router/dist/routers/127.0.0.1-8250-25500/plugin/fabric1-stub-2.0.0-rc1.jar /root/java/router/dist/routers/127.0.0.1-8250-25500/plugin/fabric1-stub-2.0.0-rc1.jar_bak

cp /root/java/WeCross-Fabric1-Stub/dist/apps/fabric1-stub-2.0.0-rc1.jar /root/java/router/dist/routers/127.0.0.1-8250-25500/plugin/

重启router服务
尝试调用,一切正常

正常启动102的几个服务

101生成二级账户报文时,在原有的properties基础上再加上三个属性,102也是

{
  "data" : {
    "luyuSign" : "",
    "type" : "ECDSA_SECP256R1_WITH_SHA256",
    "nonce" : 1640067564335,
    "identity" : "0x1f80e895ba2387a29ba9723b6fb3d6eded5e73c3",
    "pubKey" : "BKNRFv63jahYEMeUoFlQLnuY/7D8GsAbyUZFU9NX6qQq1Qz4AHV9PDV6WOvFTFmD2/FKJnj/923NlHsiHQT7EoE=",
    "secKey" : "ZfJGgdwZiDlpuf1wCGwGxsUL7FXJqmjGZIGatspXK/U=",
    "properties" : {
      "Fabric1.4:payment1.fabric101:cert" : "-----BEGIN CERTIFICATE-----\nMIICKTCCAc+gAwIBAgIQNR0imAw5wnfxLdsjqDCpeDAKBggqhkjOPQQDAjBzMQsw\nCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy\nYW5jaXNjbzEZMBcGA1UEChMQb3JnMS5leGFtcGxlLmNvbTEcMBoGA1UEAxMTY2Eu\nb3JnMS5leGFtcGxlLmNvbTAeFw0yMTEyMjEwNTU3MDBaFw0zMTEyMTkwNTU3MDBa\nMGsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1T\nYW4gRnJhbmNpc2NvMQ4wDAYDVQQLEwVhZG1pbjEfMB0GA1UEAwwWQWRtaW5Ab3Jn\nMS5leGFtcGxlLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABKNRFv63jahY\nEMeUoFlQLnuY/7D8GsAbyUZFU9NX6qQq1Qz4AHV9PDV6WOvFTFmD2/FKJnj/923N\nlHsiHQT7EoGjTTBLMA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMCsGA1Ud\nIwQkMCKAILdoIXfTrRdnX6kgw7mPBl8bcAOe78P9cFpQ0kb6xebOMAoGCCqGSM49\nBAMCA0gAMEUCIQCF8rxoaEPHNIPRpi6EeMfRhY+qFCOnIV8lXKa7o+lIWQIgKd1Y\nzwE3J1jXhx67I5vNWZO62LFx7j8z640P1l3oL3A=\n-----END CERTIFICATE-----\n",
      "Fabric1.4:payment1.fabric101:name" : "fabric_admin",
      "Fabric1.4:payment1.fabric101:mspid" : "Org1MSP",
      

  "Fabric1.4:payment2.fabric102:cert" : "-----BEGIN CERTIFICATE-----\nMIICKTCCAc+gAwIBAgIQNR0imAw5wnfxLdsjqDCpeDAKBggqhkjOPQQDAjBzMQsw\nCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy\nYW5jaXNjbzEZMBcGA1UEChMQb3JnMS5leGFtcGxlLmNvbTEcMBoGA1UEAxMTY2Eu\nb3JnMS5leGFtcGxlLmNvbTAeFw0yMTEyMjEwNTU3MDBaFw0zMTEyMTkwNTU3MDBa\nMGsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1T\nYW4gRnJhbmNpc2NvMQ4wDAYDVQQLEwVhZG1pbjEfMB0GA1UEAwwWQWRtaW5Ab3Jn\nMS5leGFtcGxlLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABKNRFv63jahY\nEMeUoFlQLnuY/7D8GsAbyUZFU9NX6qQq1Qz4AHV9PDV6WOvFTFmD2/FKJnj/923N\nlHsiHQT7EoGjTTBLMA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMCsGA1Ud\nIwQkMCKAILdoIXfTrRdnX6kgw7mPBl8bcAOe78P9cFpQ0kb6xebOMAoGCCqGSM49\nBAMCA0gAMEUCIQCF8rxoaEPHNIPRpi6EeMfRhY+qFCOnIV8lXKa7o+lIWQIgKd1Y\nzwE3J1jXhx67I5vNWZO62LFx7j8z640P1l3oL3A=\n-----END CERTIFICATE-----\n",
  "Fabric1.4:payment2.fabric102:name" : "fabric_admin",
  "Fabric1.4:payment2.fabric102:mspid" : "Org1MSP"

},
"isDefault" : true

  }
}

102:

{
  "data" : {
    "luyuSign" : "",
    "type" : "ECDSA_SECP256R1_WITH_SHA256",
    "nonce" : 1640068235326,
    "identity" : "0xfca9d31bad28ca5d754652b28a9d9ada7cabdfff",
    "pubKey" : "BKNRFv63jahYEMeUoFlQLnuY/7D8GsAbyUZFU9NX6qQq1Qz4AHV9PDV6WOvFTFmD2/FKJnj/923NlHsiHQT7EoE=",
    "secKey" : "ZfJGgdwZiDlpuf1wCGwGxsUL7FXJqmjGZIGatspXK/U=",
    "properties" : {
      "Fabric1.4:payment2.fabric102:name" : "fabric_admin",
      "Fabric1.4:payment2.fabric102:mspid" : "Org1MSP",
      "Fabric1.4:payment2.fabric102:cert" : "-----BEGIN CERTIFICATE-----\nMIICKTCCAc+gAwIBAgIQNR0imAw5wnfxLdsjqDCpeDAKBggqhkjOPQQDAjBzMQsw\nCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy\nYW5jaXNjbzEZMBcGA1UEChMQb3JnMS5leGFtcGxlLmNvbTEcMBoGA1UEAxMTY2Eu\nb3JnMS5leGFtcGxlLmNvbTAeFw0yMTEyMjEwNTU3MDBaFw0zMTEyMTkwNTU3MDBa\nMGsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1T\nYW4gRnJhbmNpc2NvMQ4wDAYDVQQLEwVhZG1pbjEfMB0GA1UEAwwWQWRtaW5Ab3Jn\nMS5leGFtcGxlLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABKNRFv63jahY\nEMeUoFlQLnuY/7D8GsAbyUZFU9NX6qQq1Qz4AHV9PDV6WOvFTFmD2/FKJnj/923N\nlHsiHQT7EoGjTTBLMA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMCsGA1Ud\nIwQkMCKAILdoIXfTrRdnX6kgw7mPBl8bcAOe78P9cFpQ0kb6xebOMAoGCCqGSM49\nBAMCA0gAMEUCIQCF8rxoaEPHNIPRpi6EeMfRhY+qFCOnIV8lXKa7o+lIWQIgKd1Y\nzwE3J1jXhx67I5vNWZO62LFx7j8z640P1l3oL3A=\n-----END CERTIFICATE-----\n",
      

  "Fabric1.4:payment1.fabric101:name" : "fabric_admin",
  "Fabric1.4:payment1.fabric101:mspid" : "Org1MSP",
  "Fabric1.4:payment1.fabric101:cert" : "-----BEGIN CERTIFICATE-----\nMIICKTCCAc+gAwIBAgIQNR0imAw5wnfxLdsjqDCpeDAKBggqhkjOPQQDAjBzMQsw\nCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy\nYW5jaXNjbzEZMBcGA1UEChMQb3JnMS5leGFtcGxlLmNvbTEcMBoGA1UEAxMTY2Eu\nb3JnMS5leGFtcGxlLmNvbTAeFw0yMTEyMjEwNTU3MDBaFw0zMTEyMTkwNTU3MDBa\nMGsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1T\nYW4gRnJhbmNpc2NvMQ4wDAYDVQQLEwVhZG1pbjEfMB0GA1UEAwwWQWRtaW5Ab3Jn\nMS5leGFtcGxlLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABKNRFv63jahY\nEMeUoFlQLnuY/7D8GsAbyUZFU9NX6qQq1Qz4AHV9PDV6WOvFTFmD2/FKJnj/923N\nlHsiHQT7EoGjTTBLMA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMCsGA1Ud\nIwQkMCKAILdoIXfTrRdnX6kgw7mPBl8bcAOe78P9cFpQ0kb6xebOMAoGCCqGSM49\nBAMCA0gAMEUCIQCF8rxoaEPHNIPRpi6EeMfRhY+qFCOnIV8lXKa7o+lIWQIgKd1Y\nzwE3J1jXhx67I5vNWZO62LFx7j8z640P1l3oL3A=\n-----END CERTIFICATE-----\n"

},
"isDefault" : true

  }
}

这样就可以在两个链都使用org1admin用户,也不需要再互相添加账户

编写合约:
chcc.go

package main

import (
    "fmt"
    "github.com/hyperledger/fabric/core/chaincode/shim"
    pb "github.com/hyperledger/fabric/protos/peer"
    "strconv"
)

type Chcc struct {
}

func main() {
    err := shim.Start(new(Chcc))
    if err != nil {
        fmt.Printf("Error starting Simple chaincode: %s", err)
    }
}

const (
    ccbt  = "callCallBackTimes"            //用来计数,回调了call方法多少次
    stcbt = "sendTransactionCallBackTimes" //用来计数,回调了sendTransaction方法多少次
    aval  = "fabric102AValue"
)

func (c *Chcc) Init(stub shim.ChaincodeStubInterface) pb.Response {
    z := strconv.Itoa(0)
    //两个次数都设成0
    stub.PutState(ccbt, []byte(z))
    stub.PutState(stcbt, []byte(z))
    return shim.Success(nil)
}

func (c *Chcc) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
    function, args := stub.GetFunctionAndParameters()
    //调用查询方法,直接返回值
    if function == "callFabric102" {
        return c.callFabric102(stub, args)
        //调用sengTransaction方法
    } else if function == "sendTxToFabric102" {
        return c.sendTxToFabric102(stub, args)
        //查询调用次数
    } else if function == "getLuYuInvokeTimes" {
        return c.getLuYuInvokeTimes(stub, args)
    } else if function == "getCallback" {
        var nonce uint64
        nonce, callbackArgs, err := ParseCallbackArgs(args)
        if err != nil {
            return shim.Error(err.Error())
        }
        return c.getCallback(stub, nonce, callbackArgs)
    } else if function == "setCallback" {
        var nonce uint64
        var callbackArgs []string
        nonce, callbackArgs, err := ParseCallbackArgs(args)
        if err != nil {

        }
        return c.setCallback(stub, nonce, callbackArgs)
    }
    return shim.Error("Invalid invoke function name.")

}

func (c *Chcc) callFabric102(stub shim.ChaincodeStubInterface, args []string) pb.Response {
    //调用CrossCall方法会生成_event_call的event,plugin捕捉到这个event之后就会向对应的路径资源发送
    nonce, err := CrossCall(stub, "payment2.fabric102.mycc", "query",
        []string{"a"}, "0x1f80e895ba2387a29ba9723b6fb3d6eded5e73c3", "getCallback")
    if err != nil {
        return shim.Error("call error:" + err.Error())
    }
    return shim.Success([]byte(fmt.Sprintf("nonce: %d", nonce)))
}

func (c *Chcc) sendTxToFabric102(stub shim.ChaincodeStubInterface, args []string) pb.Response {
    //
    nonce, err := CrossSendTransaction(stub, "payment2.fabric102.mycc", "invoke",
        []string{"b", "a", "1"}, "0x1f80e895ba2387a29ba9723b6fb3d6eded5e73c3", "setCallback")
    if err != nil {
        return shim.Error("call error:" + err.Error())
    }
    return shim.Success([]byte(fmt.Sprintf("nonce: %d", nonce)))
}

func (c *Chcc) getCallback(stub shim.ChaincodeStubInterface, nonce uint64, value []string) pb.Response {
    res, err := stub.GetState(ccbt)
    if err != nil {
        return shim.Error(err.Error())
    }
    i, _ := strconv.Atoi(string(res))
    i++
    if err := stub.PutState(ccbt, []byte(strconv.Itoa(i))); err != nil {
        return shim.Error(err.Error())
    }
    a := value[0]
    stub.PutState(aval, []byte(a))
    return shim.Success(nil)
}

func (c *Chcc) setCallback(stub shim.ChaincodeStubInterface, nonce uint64, args []string) pb.Response {
    res, err := stub.GetState(stcbt)
    if err != nil {
        return shim.Error(err.Error())
    }
    i, _ := strconv.Atoi(string(res))
    i++
    stub.PutState(stcbt, []byte(strconv.Itoa(i)))
    return shim.Success(nil)
}

func (c *Chcc) getLuYuInvokeTimes(stub shim.ChaincodeStubInterface, args []string) pb.Response {
    res1, err := stub.GetState(ccbt)
    if err != nil {
        return shim.Error(err.Error())
    }
    res2, err := stub.GetState(stcbt)
    if err != nil {
        return shim.Error(err.Error())
    }
    r1, _ := strconv.Atoi(string(res1))
    r2, _ := strconv.Atoi(string(res2))
    a, _ := stub.GetState(aval)
    res := []byte(fmt.Sprintf("callCallBackTimes = %v,sendTransactionCallBackTimes = %v, 102A-Value = %v \n",
        r1, r2, string(a)))
    return shim.Success(res)
}

cross_fabric_sdk.go(拷贝)

package main

import (
    "encoding/json"
    "errors"
    "fmt"
    "github.com/hyperledger/fabric/core/chaincode/shim"
    "math/rand"
    "strconv"
)

type CrossEvent struct {
    Path           string   `json:"path"`
    Method         string   `json:"method"`
    Args           []string `json:"args"`
    Nonce          uint64   `json:"nonce"`
    Identity       string   `json:"identity"`
    CallbackMethod string   `json:"callbackMethod"`
}

func CrossSendTransaction(stub shim.ChaincodeStubInterface, path string, method string, args []string, identity string, callbackMethod string) (uint64, error) {
    nonce := uint64(rand.Uint32())
    tx := CrossEvent{
        Path:           path,
        Method:         method,
        Args:           args,
        Identity:       identity,
        CallbackMethod: callbackMethod,
        Nonce:          nonce,
    }

    txBytes, err := json.Marshal(tx)
    if err != nil {
        return nonce, fmt.Errorf("error")
    }
    
    stub.SetEvent("_event_sendTransaction", txBytes)
    
    return nonce, nil

}

func CrossCall(stub shim.ChaincodeStubInterface, path string, method string, args []string, identity string, callbackMethod string) (uint64, error) {
    nonce := uint64(rand.Uint32())
    tx := CrossEvent{
        Path:           path,
        Method:         method,
        Args:           args,
        Identity:       identity,
        CallbackMethod: callbackMethod,
        Nonce:          nonce,
    }

    txBytes, err := json.Marshal(tx)
    if err != nil {
        return nonce, fmt.Errorf("error")
    }

    stub.SetEvent("_event_call", txBytes)
    
    return nonce, nil

}

func checkError(err error) {
    if err != nil {
        panic(err)
    }
}

func ParseCallbackArgs(args []string) (uint64, []string, error) {
    if len(args) == 0 {
        return 0, nil, errors.New("Error: Callback args[] must start with nonce")
    }
    nonce, err := strconv.ParseUint(args[0], 10, 32)
    if err != nil {
        return 0, nil, err
    }
    return nonce, args[1:], nil
}

在101部署合约:

docker cp /root/go/src/chaincode/ cli:/opt/gopath/src/github.com/chaincode/chcc/
docker exec -it cli bash

peer chaincode install -n chcc -v 1.0.0 -p github.com/chaincode/chcc/chaincode


CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp
CORE_PEER_ADDRESS=peer0.org2.example.com:9051
CORE_PEER_LOCALMSPID="Org2MSP"
CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt


peer chaincode install -n chcc -v 1.0.0 -p github.com/chaincode/chcc/chaincode


peer chaincode instantiate -o orderer.example.com:7050 --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n chcc -l golang -v 1.0.0 -c '{"Args":["init"]}'

peer chaincode query -o orderer.example.com:7050  -C mychannel -n chcc -c '{"Args":["getLuYuInvokeTimes"]}'

合约如果需要升级

peer chaincode install -n chcc -v 1.0.0 -l golang -p github.com/chaincode/chcc/chaincode
peer chaincode upgrade -o orderer.example.com:7050 --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n chcc -l golang -v 1.0.0 -c '{"Args":["init"]}'

使用postman调用chcc5,在调用callFabric102sendTxToFabric102时,都需要使用router提供的sendTransaction接口,这是因为只有sendTransaction接口才会以invoke的方式调用fabric网络,如果是call方法,则会以query的方式调用。而在fabric中,只有invoke调用是会生成区块的,只有生成了区块,才能监控到合约触发的_event_call事件。

http://192.168.92.101:8250/resource/payment1/fabric101/chcc5/call
{
    "version":"1",
    "data":{
        "path": "payment1.fabric101.chcc5",
        "method": "getLuYuInvokeTimes",
        "args": [""],
        "nonce":123456,
        "luyuSign":"",
        "sender": "0x1f80e895ba2387a29ba9723b6fb3d6eded5e73c3"
    }
}
http://192.168.92.101:8250/resource/payment1/fabric101/chcc5/sendTransaction
{
    "version":"1",
    "data":{
        "path": "payment1.fabric101.chcc5",
        "method": "callFabric102",
        "args": [],
        "nonce":123456,
        "luyuSign":"",
        "sender": "0x1f80e895ba2387a29ba9723b6fb3d6eded5e73c3"
    }
}
http://192.168.92.101:8250/resource/payment1/fabric101/chcc5/sendTransaction
{
    "version":"1",
    "data":{
        "path": "payment1.fabric101.chcc5",
        "method": "sendTxToFabric102",
        "args": [],
        "nonce":1,
        "luyuSign":"",
        "sender": "0x1f80e895ba2387a29ba9723b6fb3d6eded5e73c3"
    }
}

转载请注明来源

×

喜欢就点赞,疼爱就打赏