第三步-同构跨链: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脚本,把replacePrivateKey
和generateChannelArtifacts
单独拎出来执行(因为crypto-config
已经存在,所以replacePrivateKey
和generateChannelArtifacts
就不再执行)
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
}
}
这样就可以在两个链都使用org1
的admin
用户,也不需要再互相添加账户
编写合约: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
,在调用callFabric102
和sendTxToFabric102
时,都需要使用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"
}
}
转载请注明来源