【区块链技术与应用】(七)
创始人
2024-01-28 20:32:03
0

资料来源

https://pkg.go.dev/github.com/hyperledger/fabric-sdk-go#section-readme

https://github.com/hyperledger/fabric-sdk-go
https://wiki.hyperledger.org/display/fabric
https://github.com/hyperledger/fabric-samples

书接上回,补充getway链码分析

上期代码实战

点击上次作业链接,查看三种实例的运行结果
在这里插入图片描述

asset-transfer-basic

assetTransfer.go

/*
SPDX-License-Identifier: Apache-2.0
*/package mainimport ("log""github.com/hyperledger/fabric-contract-api-go/contractapi""github.com/hyperledger/fabric-samples/asset-transfer-basic/chaincode-go/chaincode"
)func main() {
//新建链码assetChaincode, err := contractapi.NewChaincode(&chaincode.SmartContract{})if err != nil {log.Panicf("Error creating asset-transfer-basic chaincode: %v", err)}
//启动链码if err := assetChaincode.Start(); err != nil {log.Panicf("Error starting asset-transfer-basic chaincode: %v", err)}
}

smartcontract.go

package chaincodeimport ("encoding/json""fmt""github.com/hyperledger/fabric-contract-api-go/contractapi"
)// SmartContract provides functions for managing an Asset
//链码结构
type SmartContract struct {contractapi.Contract
}// Asset describes basic details of what makes up a simple asset
//Insert struct field in alphabetic order => to achieve determinism across languages
// golang keeps the order when marshal to json but doesn't order automatically
type Asset struct {AppraisedValue int    `json:"AppraisedValue"`Color          string `json:"Color"`ID             string `json:"ID"`Owner          string `json:"Owner"`Size           int    `json:"Size"`
}// InitLedger adds a base set of assets to the ledger
func (s *SmartContract) InitLedger(ctx contractapi.TransactionContextInterface) error {assets := []Asset{{ID: "asset1", Color: "blue", Size: 5, Owner: "Tomoko", AppraisedValue: 300},{ID: "asset2", Color: "red", Size: 5, Owner: "Brad", AppraisedValue: 400},{ID: "asset3", Color: "green", Size: 10, Owner: "Jin Soo", AppraisedValue: 500},{ID: "asset4", Color: "yellow", Size: 10, Owner: "Max", AppraisedValue: 600},{ID: "asset5", Color: "black", Size: 15, Owner: "Adriana", AppraisedValue: 700},{ID: "asset6", Color: "white", Size: 15, Owner: "Michel", AppraisedValue: 800},}for _, asset := range assets {assetJSON, err := json.Marshal(asset)if err != nil {return err}
//存放数据err = ctx.GetStub().PutState(asset.ID, assetJSON)if err != nil {return fmt.Errorf("failed to put to world state. %v", err)}}return nil
}// CreateAsset issues a new asset to the world state with given details.
//查看代码中的创建资产函数名称及参数
func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface, id string, color string, size int, owner string, appraisedValue int) error {exists, err := s.AssetExists(ctx, id)if err != nil {return err}if exists {return fmt.Errorf("the asset %s already exists", id)}asset := Asset{ID:             id,Color:          color,Size:           size,Owner:          owner,AppraisedValue: appraisedValue,}assetJSON, err := json.Marshal(asset)if err != nil {return err}return ctx.GetStub().PutState(id, assetJSON)
}// ReadAsset returns the asset stored in the world state with given id.
func (s *SmartContract) ReadAsset(ctx contractapi.TransactionContextInterface, id string) (*Asset, error) {assetJSON, err := ctx.GetStub().GetState(id)if err != nil {return nil, fmt.Errorf("failed to read from world state: %v", err)}if assetJSON == nil {return nil, fmt.Errorf("the asset %s does not exist", id)}var asset Asseterr = json.Unmarshal(assetJSON, &asset)if err != nil {return nil, err}return &asset, nil
}// UpdateAsset updates an existing asset in the world state with provided parameters.
func (s *SmartContract) UpdateAsset(ctx contractapi.TransactionContextInterface, id string, color string, size int, owner string, appraisedValue int) error {exists, err := s.AssetExists(ctx, id)if err != nil {return err}if !exists {return fmt.Errorf("the asset %s does not exist", id)}// overwriting original asset with new assetasset := Asset{ID:             id,Color:          color,Size:           size,Owner:          owner,AppraisedValue: appraisedValue,}assetJSON, err := json.Marshal(asset)if err != nil {return err}return ctx.GetStub().PutState(id, assetJSON)
}// DeleteAsset deletes an given asset from the world state.
func (s *SmartContract) DeleteAsset(ctx contractapi.TransactionContextInterface, id string) error {exists, err := s.AssetExists(ctx, id)if err != nil {return err}if !exists {return fmt.Errorf("the asset %s does not exist", id)}return ctx.GetStub().DelState(id)
}// AssetExists returns true when asset with given ID exists in world state
func (s *SmartContract) AssetExists(ctx contractapi.TransactionContextInterface, id string) (bool, error) {assetJSON, err := ctx.GetStub().GetState(id)if err != nil {return false, fmt.Errorf("failed to read from world state: %v", err)}return assetJSON != nil, nil
}// TransferAsset updates the owner field of asset with given id in world state, and returns the old owner.
func (s *SmartContract) TransferAsset(ctx contractapi.TransactionContextInterface, id string, newOwner string) (string, error) {asset, err := s.ReadAsset(ctx, id)if err != nil {return "", err}oldOwner := asset.Ownerasset.Owner = newOwnerassetJSON, err := json.Marshal(asset)if err != nil {return "", err}err = ctx.GetStub().PutState(id, assetJSON)if err != nil {return "", err}return oldOwner, nil
}// GetAllAssets returns all assets found in world state
func (s *SmartContract) GetAllAssets(ctx contractapi.TransactionContextInterface) ([]*Asset, error) {// range query with empty string for startKey and endKey does an// open-ended query of all assets in the chaincode namespace.resultsIterator, err := ctx.GetStub().GetStateByRange("", "")if err != nil {return nil, err}defer resultsIterator.Close()var assets []*Assetfor resultsIterator.HasNext() {queryResponse, err := resultsIterator.Next()if err != nil {return nil, err}var asset Asseterr = json.Unmarshal(queryResponse.Value, &asset)if err != nil {return nil, err}assets = append(assets, &asset)}return assets, nil
}

asset-transfer-events

connect.go

/*
Copyright 2022 IBM All Rights Reserved.SPDX-License-Identifier: Apache-2.0
*/package mainimport ("crypto/x509""fmt""os""path""github.com/hyperledger/fabric-gateway/pkg/identity""google.golang.org/grpc""google.golang.org/grpc/credentials"
)const (mspID        = "Org1MSP"cryptoPath   = "../../test-network/organizations/peerOrganizations/org1.example.com"certPath     = cryptoPath + "/users/User1@org1.example.com/msp/signcerts/cert.pem"keyPath      = cryptoPath + "/users/User1@org1.example.com/msp/keystore/"tlsCertPath  = cryptoPath + "/peers/peer0.org1.example.com/tls/ca.crt"peerEndpoint = "localhost:7051"gatewayPeer  = "peer0.org1.example.com"
)// newGrpcConnection creates a gRPC connection to the Gateway server.
func newGrpcConnection() *grpc.ClientConn {certificate, err := loadCertificate(tlsCertPath)if err != nil {panic(err)}certPool := x509.NewCertPool()certPool.AddCert(certificate)transportCredentials := credentials.NewClientTLSFromCert(certPool, gatewayPeer)connection, err := grpc.Dial(peerEndpoint, grpc.WithTransportCredentials(transportCredentials))if err != nil {panic(fmt.Errorf("failed to create gRPC connection: %w", err))}return connection
}// newIdentity creates a client identity for this Gateway connection using an X.509 certificate.
func newIdentity() *identity.X509Identity {certificate, err := loadCertificate(certPath)if err != nil {panic(err)}id, err := identity.NewX509Identity(mspID, certificate)if err != nil {panic(err)}return id
}func loadCertificate(filename string) (*x509.Certificate, error) {certificatePEM, err := os.ReadFile(filename)if err != nil {return nil, fmt.Errorf("failed to read certificate file: %w", err)}return identity.CertificateFromPEM(certificatePEM)
}// newSign creates a function that generates a digital signature from a message digest using a private key.
func newSign() identity.Sign {files, err := os.ReadDir(keyPath)if err != nil {panic(fmt.Errorf("failed to read private key directory: %w", err))}privateKeyPEM, err := os.ReadFile(path.Join(keyPath, files[0].Name()))if err != nil {panic(fmt.Errorf("failed to read private key file: %w", err))}privateKey, err := identity.PrivateKeyFromPEM(privateKeyPEM)if err != nil {panic(err)}sign, err := identity.NewPrivateKeySign(privateKey)if err != nil {panic(err)}return sign
}

app.go

/*
Copyright 2022 IBM All Rights Reserved.SPDX-License-Identifier: Apache-2.0
*/package mainimport ("bytes""context""encoding/json""errors""fmt""time""github.com/hyperledger/fabric-gateway/pkg/client"
)const (channelName   = "mychannel"chaincodeName = "events"
)var now = time.Now()
var assetID = fmt.Sprintf("asset%d", now.Unix()*1e3+int64(now.Nanosecond())/1e6)func main() {clientConnection := newGrpcConnection()defer clientConnection.Close()id := newIdentity()sign := newSign()gateway, err := client.Connect(id,client.WithSign(sign),client.WithClientConnection(clientConnection),client.WithEvaluateTimeout(5*time.Second),client.WithEndorseTimeout(15*time.Second),client.WithSubmitTimeout(5*time.Second),client.WithCommitStatusTimeout(1*time.Minute),)if err != nil {panic(err)}defer gateway.Close()network := gateway.GetNetwork(channelName)contract := network.GetContract(chaincodeName)// Context used for event listeningctx, cancel := context.WithCancel(context.Background())defer cancel()// Listen for events emitted by subsequent transactionsstartChaincodeEventListening(ctx, network)firstBlockNumber := createAsset(contract)updateAsset(contract)transferAsset(contract)deleteAsset(contract)// Replay events from the block containing the first transactionreplayChaincodeEvents(ctx, network, firstBlockNumber)
}
//开始监听
func startChaincodeEventListening(ctx context.Context, network *client.Network) {fmt.Println("\n*** Start chaincode event listening")events, err := network.ChaincodeEvents(ctx, chaincodeName)if err != nil {panic(fmt.Errorf("failed to start chaincode event listening: %w", err))}go func() {for event := range events {asset := formatJSON(event.Payload)fmt.Printf("\n<-- Chaincode event received: %s - %s\n", event.EventName, asset)}}()
}func formatJSON(data []byte) string {var result bytes.Bufferif err := json.Indent(&result, data, "", "  "); err != nil {panic(fmt.Errorf("failed to parse JSON: %w", err))}return result.String()
}func createAsset(contract *client.Contract) uint64 {fmt.Printf("\n--> Submit transaction: CreateAsset, %s owned by Sam with appraised value 100\n", assetID)_, commit, err := contract.SubmitAsync("CreateAsset", client.WithArguments(assetID, "blue", "10", "Sam", "100"))if err != nil {panic(fmt.Errorf("failed to submit transaction: %w", err))}status, err := commit.Status()if err != nil {panic(fmt.Errorf("failed to get transaction commit status: %w", err))}if !status.Successful {panic(fmt.Errorf("failed to commit transaction with status code %v", status.Code))}fmt.Println("\n*** CreateAsset committed successfully")return status.BlockNumber
}func updateAsset(contract *client.Contract) {fmt.Printf("\n--> Submit transaction: UpdateAsset, %s update appraised value to 200\n", assetID)_, err := contract.SubmitTransaction("UpdateAsset", assetID, "blue", "10", "Sam", "200")if err != nil {panic(fmt.Errorf("failed to submit transaction: %w", err))}fmt.Println("\n*** UpdateAsset committed successfully")
}func transferAsset(contract *client.Contract) {fmt.Printf("\n--> Submit transaction: TransferAsset, %s to Mary\n", assetID)_, err := contract.SubmitTransaction("TransferAsset", assetID, "Mary")if err != nil {panic(fmt.Errorf("failed to submit transaction: %w", err))}fmt.Println("\n*** TransferAsset committed successfully")
}func deleteAsset(contract *client.Contract) {fmt.Printf("\n--> Submit transaction: DeleteAsset, %s\n", assetID)_, err := contract.SubmitTransaction("DeleteAsset", assetID)if err != nil {panic(fmt.Errorf("failed to submit transaction: %w", err))}fmt.Println("\n*** DeleteAsset committed successfully")
}func replayChaincodeEvents(ctx context.Context, network *client.Network, startBlock uint64) {fmt.Println("\n*** Start chaincode event replay")events, err := network.ChaincodeEvents(ctx, chaincodeName, client.WithStartBlock(startBlock))if err != nil {panic(fmt.Errorf("failed to start chaincode event listening: %w", err))}for {select {case <-time.After(10 * time.Second):panic(errors.New("timeout waiting for event replay"))case event := <-events:asset := formatJSON(event.Payload)fmt.Printf("\n<-- Chaincode event replayed: %s - %s\n", event.EventName, asset)if event.EventName == "DeleteAsset" {// Reached the last submitted transaction so return to stop listening for eventsreturn}}}
}

SDK运行

参见SDK运行实战,这是之前实验的内容,在此不多赘述。

https://algernon98.github.io/post/%E5%8C%BA%E5%9D%97%E9%93%BE4

在这里插入图片描述

cd .. && go build && ./fabric-go-sdk
>> 开始创建通道......
>>>> 使用每个org的管理员身份更新锚节点配置...
>>>> 使用每个org的管理员身份更新锚节点配置完成
>> 创建通道成功
>> 加入通道......
>> 加入通道成功
>> 开始打包链码......
>> 打包链码成功
>> 开始安装链码......
>> 安装链码成功
>> 组织认可智能合约定义......
>>> chaincode approved by Org1 peers:peer0.org1.example.com:7051peer1.org1.example.com:9051
>> 组织认可智能合约定义完成
>> 检查智能合约是否就绪......
LifecycleCheckCCCommitReadiness cc = simplecc, = {map[Org1MSP:true]}
LifecycleCheckCCCommitReadiness cc = simplecc, = {map[Org1MSP:true]}
>> 智能合约已经就绪
>> 提交智能合约定义......
>> 智能合约定义提交完成
>> 调用智能合约初始化方法......
>> 完成智能合约初始化
>> 通过链码外部服务设置链码状态......
>> 设置链码状态完成
<--- 添加信息 --->: 18c0c86ce029d7de04461484976c5151992864b52ca28905d0ccf911443fdfcb
<--- 查询信息 --->: 123---------------------------------
作者: Algernon
本文来自于: https://algernon98.github.io/
博客内容遵循 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 协议

在这里插入图片描述

sdk链码分析

chaincode

https://github.com/sxguan/fabric-go-sdk/blob/main/chaincode/chaincode.go

完整代码

package mainimport ("encoding/json""fmt""github.com/hyperledger/fabric-chaincode-go/shim""github.com/hyperledger/fabric-protos-go/peer"
)// SimpleAsset implements a simple chaincode to manage an asset
type SimpleAsset struct {
}
type outputEvent struct {EventName string
}// Init is called during chaincode instantiation to initialize any
// data. Note that chaincode upgrade also calls this function to reset
// or to migrate data.
func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response {fmt.Printf("init...")return shim.Success(nil)
}// Invoke is called per transaction on the chaincode. Each transaction is
// either a 'get' or a 'set' on the asset created by Init function. The Set
// method may create a new asset by specifying a new key-value pair.
func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response {// Extract the function and args from the transaction proposalfn, args := stub.GetFunctionAndParameters()var result stringvar err errorif fn == "set" {result, err = set(stub, args)} else { // assume 'get' even if fn is nilresult, err = get(stub, args)}if err != nil {return shim.Error(err.Error())}// Return the result as success payloadreturn shim.Success([]byte(result))
}// Set stores the asset (both key and value) on the ledger. If the key exists,
// it will override the value with the new one
func set(stub shim.ChaincodeStubInterface, args []string) (string, error) {if len(args) != 2 {return "", fmt.Errorf("Incorrect arguments. Expecting a key and a value")}err := stub.PutState(args[0], []byte(args[1]))if err != nil {return "", fmt.Errorf("Failed to set asset: %s", args[0])}event := outputEvent{EventName: "set",}payload, err := json.Marshal(event)if err != nil {return "", err}err = stub.SetEvent("chaincode-event", payload)return args[1], nil
}// Get returns the value of the specified asset key
func get(stub shim.ChaincodeStubInterface, args []string) (string, error) {if len(args) != 1 {return "", fmt.Errorf("Incorrect arguments. Expecting a key")}value, err := stub.GetState(args[0])if err != nil {return "", fmt.Errorf("Failed to get asset: %s with error: %s", args[0], err)}if value == nil {return "", fmt.Errorf("Asset not found: %s", args[0])}return string(value), nil
}// main function starts up the chaincode in the container during instantiate
func main() {if err := shim.Start(new(SimpleAsset)); err != nil {fmt.Printf("Error starting SimpleAsset chaincode: %s", err)}
}

逐个分析

现在我们来逐步分析代码:

导入所需包

之后的函数中都有这一部分,如遇到import导入部分则省略。


import ("encoding/json""fmt""github.com/hyperledger/fabric-chaincode-go/shim""github.com/hyperledger/fabric-protos-go/peer"
)

integration

https://github.com/sxguan/fabric-go-sdk/blob/main/sdkInit/integration.go

完整代码

package sdkInitimport ("encoding/hex""fmt""github.com/hyperledger/fabric-sdk-go/pkg/client/channel""github.com/hyperledger/fabric-sdk-go/pkg/client/event""github.com/hyperledger/fabric-sdk-go/pkg/common/errors/retry""github.com/hyperledger/fabric-sdk-go/pkg/common/errors/status"contextAPI "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/context"fabAPI "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab"contextImpl "github.com/hyperledger/fabric-sdk-go/pkg/context""github.com/hyperledger/fabric-sdk-go/pkg/fabsdk""log"
)func DiscoverLocalPeers(ctxProvider contextAPI.ClientProvider, expectedPeers int) ([]fabAPI.Peer, error) {ctx, err := contextImpl.NewLocal(ctxProvider)if err != nil {return nil, fmt.Errorf("error creating local context: %v", err)}discoveredPeers, err := retry.NewInvoker(retry.New(retry.TestRetryOpts)).Invoke(func() (interface{}, error) {peers, serviceErr := ctx.LocalDiscoveryService().GetPeers()if serviceErr != nil {return nil, fmt.Errorf("getting peers for MSP [%s] error: %v", ctx.Identifier().MSPID, serviceErr)}if len(peers) < expectedPeers {return nil, status.New(status.TestStatus, status.GenericTransient.ToInt32(), fmt.Sprintf("Expecting %d peers but got %d", expectedPeers, len(peers)), nil)}return peers, nil},)if err != nil {return nil, err}return discoveredPeers.([]fabAPI.Peer), nil
}
func (t *SdkEnvInfo) InitService(chaincodeID, channelID string, org *OrgInfo, sdk *fabsdk.FabricSDK) error {handler := &SdkEnvInfo{ChaincodeID: chaincodeID,}//prepare channel client context using client contextclientChannelContext := sdk.ChannelContext(channelID, fabsdk.WithUser(org.OrgUser), fabsdk.WithOrg(org.OrgName))// Channel client is used to query and execute transactions (Org1 is default org)var err errort.ChClient, err = channel.New(clientChannelContext)if err != nil {return err}t.EvClient, err = event.New(clientChannelContext, event.WithBlockEvents())if err != nil {return err}handler.ChClient = t.ChClienthandler.EvClient = t.EvClientreturn nil
}func regitserEvent(client *event.Client, chaincodeID string) (fabAPI.Registration, <-chan *fabAPI.CCEvent) {eventName := "chaincode-event"reg, notifier, err := client.RegisterChaincodeEvent(chaincodeID, eventName)if err != nil {fmt.Println("注册链码事件失败: %s", err)}return reg, notifier
}
func ChainCodeEventListener(c *event.Client, ccID string) fabAPI.Registration {reg, notifier := regitserEvent(c, ccID)// consume eventgo func() {for e := range notifier {log.Printf("Receive cc event, ccid: %v \neventName: %v\n"+"payload: %v \ntxid: %v \nblock: %v \nsourceURL: %v\n",e.ChaincodeID, e.EventName, string(e.Payload), e.TxID, e.BlockNumber, e.SourceURL)}}()return reg
}func TxListener(c *event.Client, txIDCh chan string) {log.Println("Transaction listener start")defer log.Println("Transaction listener exit")for id := range txIDCh {// Register monitor transaction eventlog.Printf("Register transaction event for: %v", id)txReg, txCh, err := c.RegisterTxStatusEvent(id)if err != nil {log.Printf("Register transaction event error: %v", err)continue}defer c.Unregister(txReg)// Receive transaction eventgo func() {for e := range txCh {log.Printf("Receive transaction event: txid: %v, "+"validation code: %v, block number: %v",e.TxID,e.TxValidationCode,e.BlockNumber)}}()}
}func BlockListener(ec *event.Client) fabAPI.Registration {// Register monitor block eventbeReg, beCh, err := ec.RegisterBlockEvent()if err != nil {log.Printf("Register block event error: %v", err)}log.Println("Registered block event")// Receive block eventgo func() {for e := range beCh {log.Printf("Receive block event:\nSourceURL: %v\nNumber: %v\nHash"+": %v\nPreviousHash: %v\n\n",e.SourceURL,e.Block.Header.Number,hex.EncodeToString(e.Block.Header.DataHash),hex.EncodeToString(e.Block.Header.PreviousHash))}}()return beReg
}

逐个分析

查找本地结点
func DiscoverLocalPeers(ctxProvider contextAPI.ClientProvider, expectedPeers int) ([]fabAPI.Peer, error) {ctx, err := contextImpl.NewLocal(ctxProvider)if err != nil {return nil, fmt.Errorf("error creating local context: %v", err)}discoveredPeers, err := retry.NewInvoker(retry.New(retry.TestRetryOpts)).Invoke(func() (interface{}, error) {peers, serviceErr := ctx.LocalDiscoveryService().GetPeers()if serviceErr != nil {return nil, fmt.Errorf("getting peers for MSP [%s] error: %v", ctx.Identifier().MSPID, serviceErr)}if len(peers) < expectedPeers {return nil, status.New(status.TestStatus, status.GenericTransient.ToInt32(), fmt.Sprintf("Expecting %d peers but got %d", expectedPeers, len(peers)), nil)}return peers, nil},)if err != nil {return nil, err}return discoveredPeers.([]fabAPI.Peer), nil
}

main

完整代码

package mainimport ("fabric-go-sdk/sdkInit""fmt""os""time"
)const (cc_name    = "simplecc"cc_version = "1.0.0"
)var App sdkInit.Applicationfunc main() {// init orgs information 初始化组织信息orgs := []*sdkInit.OrgInfo{{OrgAdminUser:  "Admin",OrgName:       "Org1",OrgMspId:      "Org1MSP",OrgUser:       "User1",OrgPeerNum:    2,OrgAnchorFile: "/root/go/src/fabric-go-sdk/fixtures/channel-artifacts/Org1MSPanchors.tx",},}// init sdk env info 初始化sdk相关信息info := sdkInit.SdkEnvInfo{ChannelID:        "mychannel",ChannelConfig:    "/root/go/src/fabric-go-sdk/fixtures/channel-artifacts/channel.tx",Orgs:             orgs,OrdererAdminUser: "Admin",OrdererOrgName:   "OrdererOrg",OrdererEndpoint:  "orderer.example.com",ChaincodeID:      cc_name,ChaincodePath:    "/root/go/src/fabric-go-sdk/chaincode/",ChaincodeVersion: cc_version,}// sdk setup  调用setup方法将sdk初始化sdk, err := sdkInit.Setup("config.yaml", &info)if err != nil {fmt.Println(">> SDK setup error:", err)os.Exit(-1)}// create channel and join  调用CreateAndJoinChannel方法,创建并加入通道if err := sdkInit.CreateAndJoinChannel(&info); err != nil {fmt.Println(">> Create channel and join error:", err)os.Exit(-1)}// create chaincode lifecycle  调用CreateCCLifecycle方法实现链码生命周期if err := sdkInit.CreateCCLifecycle(&info, 1, false, sdk); err != nil {fmt.Println(">> create chaincode lifecycle error: %v", err)os.Exit(-1)}// invoke chaincode set statusfmt.Println(">> 通过链码外部服务设置链码状态......")if err := info.InitService(info.ChaincodeID, info.ChannelID, info.Orgs[0], sdk); err != nil {fmt.Println("InitService successful")os.Exit(-1)}App = sdkInit.Application{SdkEnvInfo: &info,}fmt.Println(">> 设置链码状态完成")defer info.EvClient.Unregister(sdkInit.BlockListener(info.EvClient))defer info.EvClient.Unregister(sdkInit.ChainCodeEventListener(info.EvClient, info.ChaincodeID))a := []string{"set", "ID1", "123"}ret, err := App.Set(a)if err != nil {fmt.Println(err)}fmt.Println("<--- 添加信息 --->:", ret)a = []string{"set", "ID2", "456"}ret, err = App.Set(a)if err != nil {fmt.Println(err)}fmt.Println("<--- 添加信息 --->:", ret)a = []string{"set", "ID3", "789"}ret, err = App.Set(a)if err != nil {fmt.Println(err)}fmt.Println("<--- 添加信息 --->:", ret)a = []string{"get", "ID3"}response, err := App.Get(a)if err != nil {fmt.Println(err)}fmt.Println("<--- 查询信息 --->:", response)time.Sleep(time.Second * 10)}

逐个分析

定义链码名称与版本
const (cc_name    = "simplecc"cc_version = "1.0.0"
)

get

完整代码

package sdkInitimport ("fmt""github.com/hyperledger/fabric-sdk-go/pkg/client/channel"
)func (t *Application) Get(args []string) (string, error) {response, err := t.SdkEnvInfo.ChClient.Query(channel.Request{ChaincodeID: t.SdkEnvInfo.ChaincodeID, Fcn: args[0], Args: [][]byte{[]byte(args[1])}})if err != nil {return "", fmt.Errorf("failed to query: %v", err)}return string(response.Payload), nil
}

sdkInfo

https://github.com/sxguan/fabric-go-sdk/blob/main/sdkInit/sdkInfo.go

完整代码

注释写在代码里了,就不单拿出来讲了。

package sdkInitimport ("github.com/hyperledger/fabric-sdk-go/pkg/client/channel""github.com/hyperledger/fabric-sdk-go/pkg/client/event"mspclient "github.com/hyperledger/fabric-sdk-go/pkg/client/msp""github.com/hyperledger/fabric-sdk-go/pkg/client/resmgmt"contextAPI "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/context"
)type OrgInfo struct {OrgAdminUser          string // like "Admin"OrgName               string // like "Org1"OrgMspId              string // like "Org1MSP"OrgUser               string // like "User1"orgMspClient          *mspclient.ClientOrgAdminClientContext *contextAPI.ClientProviderOrgResMgmt            *resmgmt.ClientOrgPeerNum            int//Peers                 []*fab.PeerOrgAnchorFile string // like ./channel-artifacts/Org2MSPanchors.tx
}type SdkEnvInfo struct {// 通道信息ChannelID     string // like "simplecc"ChannelConfig string // like os.Getenv("GOPATH") + "/src/github.com/hyperledger/fabric-samples/test-network/channel-artifacts/testchannel.tx"// 组织信息Orgs []*OrgInfo// 排序服务节点信息OrdererAdminUser     string // like "Admin"OrdererOrgName       string // like "OrdererOrg"OrdererEndpoint      stringOrdererClientContext *contextAPI.ClientProvider// 链码信息ChaincodeID      stringChaincodeGoPath  stringChaincodePath    stringChaincodeVersion stringChClient         *channel.ClientEvClient         *event.Client
}type Application struct {SdkEnvInfo *SdkEnvInfo
}

sdkSetting

完整代码


package sdkInitimport ("fmt"mb "github.com/hyperledger/fabric-protos-go/msp"pb "github.com/hyperledger/fabric-protos-go/peer""github.com/hyperledger/fabric-sdk-go/pkg/client/channel"mspclient "github.com/hyperledger/fabric-sdk-go/pkg/client/msp""github.com/hyperledger/fabric-sdk-go/pkg/client/resmgmt""github.com/hyperledger/fabric-sdk-go/pkg/common/errors/retry""github.com/hyperledger/fabric-sdk-go/pkg/common/errors/status""github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab""github.com/hyperledger/fabric-sdk-go/pkg/common/providers/msp""github.com/hyperledger/fabric-sdk-go/pkg/core/config"lcpackager "github.com/hyperledger/fabric-sdk-go/pkg/fab/ccpackager/lifecycle""github.com/hyperledger/fabric-sdk-go/pkg/fabsdk""github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/common/policydsl""strings"
)func Setup(configFile string, info *SdkEnvInfo) (*fabsdk.FabricSDK, error) {// Create SDK setup for the integration testsvar err errorsdk, err := fabsdk.New(config.FromFile(configFile))if err != nil {return nil, err}// 为组织获得Client句柄和Context信息for _, org := range info.Orgs {org.orgMspClient, err = mspclient.New(sdk.Context(), mspclient.WithOrg(org.OrgName))if err != nil {return nil, err}orgContext := sdk.Context(fabsdk.WithUser(org.OrgAdminUser), fabsdk.WithOrg(org.OrgName))org.OrgAdminClientContext = &orgContext// New returns a resource management client instance.resMgmtClient, err := resmgmt.New(orgContext)if err != nil {return nil, fmt.Errorf("根据指定的资源管理客户端Context创建通道管理客户端失败: %v", err)}org.OrgResMgmt = resMgmtClient}// 为Orderer获得Context信息ordererClientContext := sdk.Context(fabsdk.WithUser(info.OrdererAdminUser), fabsdk.WithOrg(info.OrdererOrgName))info.OrdererClientContext = &ordererClientContextreturn sdk, nil
}func CreateAndJoinChannel(info *SdkEnvInfo) error {fmt.Println(">> 开始创建通道......")if len(info.Orgs) == 0 {return fmt.Errorf("通道组织不能为空,请提供组织信息")}// 获得所有组织的签名信息signIds := []msp.SigningIdentity{}for _, org := range info.Orgs {// Get signing identity that is used to sign create channel requestorgSignId, err := org.orgMspClient.GetSigningIdentity(org.OrgAdminUser)if err != nil {return fmt.Errorf("GetSigningIdentity error: %v", err)}signIds = append(signIds, orgSignId)}// 创建通道if err := createChannel(signIds, info); err != nil {return fmt.Errorf("Create channel error: %v", err)}fmt.Println(">> 创建通道成功")fmt.Println(">> 加入通道......")for _, org := range info.Orgs {// 加入通道// Org peers join channelif err := org.OrgResMgmt.JoinChannel(info.ChannelID, resmgmt.WithRetry(retry.DefaultResMgmtOpts), resmgmt.WithOrdererEndpoint("orderer.example.com")); err != nil {return fmt.Errorf("%s peers failed to JoinChannel: %v", org.OrgName, err)}}fmt.Println(">> 加入通道成功")return nil
}func createChannel(signIDs []msp.SigningIdentity, info *SdkEnvInfo) error {// Channel management client is responsible for managing channels (create/update channel)chMgmtClient, err := resmgmt.New(*info.OrdererClientContext)if err != nil {return fmt.Errorf("Channel management client create error: %v", err)}// create a channel for orgchannel.txreq := resmgmt.SaveChannelRequest{ChannelID: info.ChannelID,ChannelConfigPath: info.ChannelConfig,SigningIdentities: signIDs}if _, err := chMgmtClient.SaveChannel(req, resmgmt.WithRetry(retry.DefaultResMgmtOpts), resmgmt.WithOrdererEndpoint("orderer.example.com")); err != nil {return fmt.Errorf("error should be nil for SaveChannel of orgchannel: %v", err)}fmt.Println(">>>> 使用每个org的管理员身份更新锚节点配置...")//do the same get ch client and create channel for each anchor peer as well (first for Org1MSP)for i, org := range info.Orgs {req = resmgmt.SaveChannelRequest{ChannelID: info.ChannelID,ChannelConfigPath: org.OrgAnchorFile,SigningIdentities: []msp.SigningIdentity{signIDs[i]}}if _, err = org.OrgResMgmt.SaveChannel(req, resmgmt.WithRetry(retry.DefaultResMgmtOpts), resmgmt.WithOrdererEndpoint("orderer.example.com")); err != nil {return fmt.Errorf("SaveChannel for anchor org %s error: %v", org.OrgName, err)}}fmt.Println(">>>> 使用每个org的管理员身份更新锚节点配置完成")//integration.WaitForOrdererConfigUpdate(t, configQueryClient, mc.channelID, false, lastConfigBlock)return nil
}func CreateCCLifecycle(info *SdkEnvInfo, sequence int64, upgrade bool, sdk *fabsdk.FabricSDK) error {if len(info.Orgs) == 0 {return fmt.Errorf("the number of organization should not be zero.")}// Package ccfmt.Println(">> 开始打包链码......")label, ccPkg, err := packageCC(info.ChaincodeID, info.ChaincodeVersion, info.ChaincodePath)if err != nil {return fmt.Errorf("pakcagecc error: %v", err)}packageID := lcpackager.ComputePackageID(label, ccPkg)fmt.Println(">> 打包链码成功")// Install ccfmt.Println(">> 开始安装链码......")if err := installCC(label, ccPkg, info.Orgs); err != nil {return fmt.Errorf("installCC error: %v", err)}// Get installed cc packageif err := getInstalledCCPackage(packageID, info.Orgs[0]); err != nil {return fmt.Errorf("getInstalledCCPackage error: %v", err)}// Query installed ccif err := queryInstalled(packageID, info.Orgs[0]); err != nil {return fmt.Errorf("queryInstalled error: %v", err)}fmt.Println(">> 安装链码成功")// Approve ccfmt.Println(">> 组织认可智能合约定义......")if err := approveCC(packageID, info.ChaincodeID, info.ChaincodeVersion, sequence, info.ChannelID, info.Orgs, info.OrdererEndpoint); err != nil {return fmt.Errorf("approveCC error: %v", err)}// Query approve ccif err:=queryApprovedCC(info.ChaincodeID, sequence, info.ChannelID, info.Orgs);err!=nil{return fmt.Errorf("queryApprovedCC error: %v", err)}fmt.Println(">> 组织认可智能合约定义完成")// Check commit readinessfmt.Println(">> 检查智能合约是否就绪......")if err:=checkCCCommitReadiness(packageID, info.ChaincodeID, info.ChaincodeVersion, sequence, info.ChannelID, info.Orgs); err!=nil{return fmt.Errorf("checkCCCommitReadiness error: %v", err)}fmt.Println(">> 智能合约已经就绪")// Commit ccfmt.Println(">> 提交智能合约定义......")if err:=commitCC(info.ChaincodeID, info.ChaincodeVersion, sequence, info.ChannelID, info.Orgs, info.OrdererEndpoint);err!=nil{return fmt.Errorf("commitCC error: %v", err)}// Query committed ccif err:=queryCommittedCC(info.ChaincodeID, info.ChannelID, sequence, info.Orgs); err!=nil{return fmt.Errorf("queryCommittedCC error: %v", err)}fmt.Println(">> 智能合约定义提交完成")// Init ccfmt.Println(">> 调用智能合约初始化方法......")if err:=initCC(info.ChaincodeID, upgrade, info.ChannelID, info.Orgs[0], sdk); err!=nil{return fmt.Errorf("initCC error: %v", err)}fmt.Println(">> 完成智能合约初始化")return nil
}func packageCC(ccName, ccVersion, ccpath string) (string, []byte, error) {label := ccName + "_" + ccVersiondesc := &lcpackager.Descriptor{Path:  ccpath,Type:  pb.ChaincodeSpec_GOLANG,Label: label,}ccPkg, err := lcpackager.NewCCPackage(desc)if err != nil {return "", nil, fmt.Errorf("Package chaincode source error: %v", err)}return desc.Label, ccPkg, nil
}func installCC(label string, ccPkg []byte, orgs []*OrgInfo) error {installCCReq := resmgmt.LifecycleInstallCCRequest{Label:   label,Package: ccPkg,}packageID := lcpackager.ComputePackageID(installCCReq.Label, installCCReq.Package)for _, org := range orgs {orgPeers, err := DiscoverLocalPeers(*org.OrgAdminClientContext, org.OrgPeerNum)if err != nil {fmt.Errorf("DiscoverLocalPeers error: %v", err)}if flag, _ := checkInstalled(packageID, orgPeers[0], org.OrgResMgmt); flag == false {if _, err := org.OrgResMgmt.LifecycleInstallCC(installCCReq, resmgmt.WithTargets(orgPeers...), resmgmt.WithRetry(retry.DefaultResMgmtOpts)); err != nil {return fmt.Errorf("LifecycleInstallCC error: %v", err)}}}return nil
}func getInstalledCCPackage(packageID string, org *OrgInfo) error {// use org1orgPeers, err := DiscoverLocalPeers(*org.OrgAdminClientContext, 1)if err != nil {return fmt.Errorf("DiscoverLocalPeers error: %v", err)}if _, err := org.OrgResMgmt.LifecycleGetInstalledCCPackage(packageID, resmgmt.WithTargets([]fab.Peer{orgPeers[0]}...)); err != nil {return fmt.Errorf("LifecycleGetInstalledCCPackage error: %v", err)}return nil
}func queryInstalled(packageID string, org *OrgInfo) error {orgPeers, err := DiscoverLocalPeers(*org.OrgAdminClientContext, 1)if err != nil {return fmt.Errorf("DiscoverLocalPeers error: %v", err)}resp1, err := org.OrgResMgmt.LifecycleQueryInstalledCC(resmgmt.WithTargets([]fab.Peer{orgPeers[0]}...))if err != nil {return fmt.Errorf("LifecycleQueryInstalledCC error: %v", err)}packageID1 := ""for _, t := range resp1 {if t.PackageID == packageID {packageID1 = t.PackageID}}if !strings.EqualFold(packageID, packageID1) {return fmt.Errorf("check package id error")}return nil
}func checkInstalled(packageID string, peer fab.Peer, client *resmgmt.Client) (bool, error) {flag := falseresp1, err := client.LifecycleQueryInstalledCC(resmgmt.WithTargets(peer))if err != nil {return flag, fmt.Errorf("LifecycleQueryInstalledCC error: %v", err)}for _, t := range resp1 {if t.PackageID == packageID {flag = true}}return flag, nil
}func approveCC(packageID string, ccName, ccVersion string, sequence int64, channelID string, orgs []*OrgInfo, ordererEndpoint string) error {mspIDs := []string{}for _, org := range orgs {mspIDs = append(mspIDs, org.OrgMspId)}ccPolicy := policydsl.SignedByNOutOfGivenRole(int32(len(mspIDs)), mb.MSPRole_MEMBER, mspIDs)approveCCReq := resmgmt.LifecycleApproveCCRequest{Name:              ccName,Version:           ccVersion,PackageID:         packageID,Sequence:          sequence,EndorsementPlugin: "escc",ValidationPlugin:  "vscc",SignaturePolicy:   ccPolicy,InitRequired:      true,}for _, org := range orgs{orgPeers, err := DiscoverLocalPeers(*org.OrgAdminClientContext, org.OrgPeerNum)fmt.Printf(">>> chaincode approved by %s peers:\n", org.OrgName)for _, p := range orgPeers {fmt.Printf("	%s\n", p.URL())}if err!=nil{return fmt.Errorf("DiscoverLocalPeers error: %v", err)}if _, err := org.OrgResMgmt.LifecycleApproveCC(channelID, approveCCReq, resmgmt.WithTargets(orgPeers...), resmgmt.WithOrdererEndpoint(ordererEndpoint), resmgmt.WithRetry(retry.DefaultResMgmtOpts));err != nil {fmt.Errorf("LifecycleApproveCC error: %v", err)}}return nil
}func queryApprovedCC(ccName string, sequence int64, channelID string, orgs []*OrgInfo) error {queryApprovedCCReq := resmgmt.LifecycleQueryApprovedCCRequest{Name:     ccName,Sequence: sequence,}for _, org := range orgs{orgPeers, err := DiscoverLocalPeers(*org.OrgAdminClientContext, org.OrgPeerNum)if err!=nil{return fmt.Errorf("DiscoverLocalPeers error: %v", err)}// Query approve ccfor _, p := range orgPeers {resp, err := retry.NewInvoker(retry.New(retry.TestRetryOpts)).Invoke(func() (interface{}, error) {resp1, err := org.OrgResMgmt.LifecycleQueryApprovedCC(channelID, queryApprovedCCReq, resmgmt.WithTargets(p))if err != nil {return nil, status.New(status.TestStatus, status.GenericTransient.ToInt32(), fmt.Sprintf("LifecycleQueryApprovedCC returned error: %v", err), nil)}return resp1, err},)if err != nil {return fmt.Errorf("Org %s Peer %s NewInvoker error: %v", org.OrgName, p.URL(), err)}if resp==nil{return fmt.Errorf("Org %s Peer %s Got nil invoker", org.OrgName, p.URL())}}}return nil
}func checkCCCommitReadiness(packageID string, ccName, ccVersion string, sequence int64, channelID string, orgs []*OrgInfo) error {mspIds := []string{}for _, org := range orgs {mspIds = append(mspIds, org.OrgMspId)}ccPolicy := policydsl.SignedByNOutOfGivenRole(int32(len(mspIds)), mb.MSPRole_MEMBER, mspIds)req := resmgmt.LifecycleCheckCCCommitReadinessRequest{Name:              ccName,Version:           ccVersion,//PackageID:         packageID,EndorsementPlugin: "escc",ValidationPlugin:  "vscc",SignaturePolicy:   ccPolicy,Sequence:          sequence,InitRequired:      true,}for _, org := range orgs{orgPeers, err := DiscoverLocalPeers(*org.OrgAdminClientContext, org.OrgPeerNum)if err!=nil{fmt.Errorf("DiscoverLocalPeers error: %v", err)}for _, p := range orgPeers {resp, err := retry.NewInvoker(retry.New(retry.TestRetryOpts)).Invoke(func() (interface{}, error) {resp1, err := org.OrgResMgmt.LifecycleCheckCCCommitReadiness(channelID, req, resmgmt.WithTargets(p))fmt.Printf("LifecycleCheckCCCommitReadiness cc = %v, = %v\n", ccName, resp1)if err != nil {return nil, status.New(status.TestStatus, status.GenericTransient.ToInt32(), fmt.Sprintf("LifecycleCheckCCCommitReadiness returned error: %v", err), nil)}flag := truefor _, r := range resp1.Approvals {flag = flag && r}if !flag {return nil, status.New(status.TestStatus, status.GenericTransient.ToInt32(), fmt.Sprintf("LifecycleCheckCCCommitReadiness returned : %v", resp1), nil)}return resp1, err},)if err != nil {return fmt.Errorf("NewInvoker error: %v", err)}if resp==nil{return fmt.Errorf("Got nill invoker response")}}}return nil
}func commitCC(ccName, ccVersion string, sequence int64, channelID string, orgs []*OrgInfo, ordererEndpoint string) error{mspIDs := []string{}for _, org := range orgs {mspIDs = append(mspIDs, org.OrgMspId)}ccPolicy := policydsl.SignedByNOutOfGivenRole(int32(len(mspIDs)), mb.MSPRole_MEMBER, mspIDs)req := resmgmt.LifecycleCommitCCRequest{Name:              ccName,Version:           ccVersion,Sequence:          sequence,EndorsementPlugin: "escc",ValidationPlugin:  "vscc",SignaturePolicy:   ccPolicy,InitRequired:      true,}_, err := orgs[0].OrgResMgmt.LifecycleCommitCC(channelID, req, resmgmt.WithOrdererEndpoint(ordererEndpoint), resmgmt.WithRetry(retry.DefaultResMgmtOpts))if err != nil {return fmt.Errorf("LifecycleCommitCC error: %v", err)}return nil
}func queryCommittedCC( ccName string, channelID string, sequence int64, orgs []*OrgInfo) error {req := resmgmt.LifecycleQueryCommittedCCRequest{Name: ccName,}for _, org := range orgs {orgPeers, err := DiscoverLocalPeers(*org.OrgAdminClientContext, org.OrgPeerNum)if err!=nil{return fmt.Errorf("DiscoverLocalPeers error: %v", err)}for _, p := range orgPeers {resp, err := retry.NewInvoker(retry.New(retry.TestRetryOpts)).Invoke(func() (interface{}, error) {resp1, err := org.OrgResMgmt.LifecycleQueryCommittedCC(channelID, req, resmgmt.WithTargets(p))if err != nil {return nil, status.New(status.TestStatus, status.GenericTransient.ToInt32(), fmt.Sprintf("LifecycleQueryCommittedCC returned error: %v", err), nil)}flag := falsefor _, r := range resp1 {if r.Name == ccName && r.Sequence == sequence {flag = truebreak}}if !flag {return nil, status.New(status.TestStatus, status.GenericTransient.ToInt32(), fmt.Sprintf("LifecycleQueryCommittedCC returned : %v", resp1), nil)}return resp1, err},)if err != nil {return  fmt.Errorf("NewInvoker error: %v", err)}if resp==nil{return fmt.Errorf("Got nil invoker response")}}}return nil
}func initCC(ccName string, upgrade bool, channelID string, org *OrgInfo, sdk *fabsdk.FabricSDK) error {//prepare channel client context using client contextclientChannelContext := sdk.ChannelContext(channelID, fabsdk.WithUser(org.OrgUser), fabsdk.WithOrg(org.OrgName))// Channel client is used to query and execute transactions (Org1 is default org)client, err := channel.New(clientChannelContext)if err != nil {return fmt.Errorf("Failed to create new channel client: %s", err)}// init_, err = client.Execute(channel.Request{ChaincodeID: ccName, Fcn: "init", Args: nil, IsInit: true},channel.WithRetry(retry.DefaultChannelOpts))if err != nil {return fmt.Errorf("Failed to init: %s", err)}return nil
}

逐个分析

打包链码

在被安装到peer节点之前,链码需要被打包进一个tar文件。当你创建一个链码包的时候,你需要提交一个用来创建简明易读的包描述的链码包标签。
使用fabric-go-sdk将会自动以这个格式来创建文件。
链码需要被打包进一个以 .tar.gz 文件扩展名结尾的tar文件。

func packageCC(ccName, ccVersion, ccpath string) (string, []byte, error) {label := ccName + "_" + ccVersion // 链码的标签desc := &lcpackager.Descriptor{   // 使用lcpackager包中的Descriptor结构体添加描述信息Path:  ccpath, //链码路径Type:  pb.ChaincodeSpec_GOLANG, //链码的语言Label: label, // 链码的标签}ccPkg, err := lcpackager.NewCCPackage(desc) // 使用lcpackager包中NewCCPackage方法对链码进行打包if err != nil {return "", nil, fmt.Errorf("Package chaincode source error: %v", err)}return desc.Label, ccPkg, nil
}
安装链码

你需要在每个要执行和背书交易的peer节点上安装链码包。使用SDK时,你需要以 Peer Administrator(peer所在组织的管理员) 的身份来完成这步。链码安装后,你的 peer 节点会构建链码,并且如果你的链码有问题,会返回一个构建错误。建议每个组织只打包链码一次,然后安装相同的包在属于他们组织的每一个peer节点上。如果某个通道希望确保每个组织都运行同样的链码,某一个组织可以打包链码并通过带外数据(不通过链上)把它发送给其他通道成员.
通过指令成功安装链码后会返回链码包标识符,它是包标签和包哈希值的结合。这个包标识符用来关联安装在你的peer节点上的链码包已被批准的链码。为下一步的操作保存这个标识符。你也可以查询安装在peer节点上的包来查看包标识符。

func installCC(label string, ccPkg []byte, orgs []*OrgInfo) error {installCCReq := resmgmt.LifecycleInstallCCRequest{Label:   label,Package: ccPkg,}// 使用lcpackager中的ComputePackageID方法查询并返回链码的packageIDpackageID := lcpackager.ComputePackageID(installCCReq.Label, installCCReq.Package)for _, org := range orgs {orgPeers, err := DiscoverLocalPeers(*org.OrgAdminClientContext, org.OrgPeerNum)if err != nil {fmt.Errorf("DiscoverLocalPeers error: %v", err)}// 检查是否安装链码,如果未安装则继续执行if flag, _ := checkInstalled(packageID, orgPeers[0], org.OrgResMgmt); flag == false {// 使用resmgmt中的LifecycleInstallCC方法安装链码,其中WithRetry方法为安装不成功时重试安装,DefaultResMgmtOpts为默认的重试安装规则if _, err := org.OrgResMgmt.LifecycleInstallCC(installCCReq, resmgmt.WithTargets(orgPeers...), resmgmt.WithRetry(retry.DefaultResMgmtOpts)); err != nil {return fmt.Errorf("LifecycleInstallCC error: %v", err)}}}return nil
}
//检查是否安装过链码
func checkInstalled(packageID string, peer fab.Peer, client *resmgmt.Client) (bool, error) {flag := falseresp1, err := client.LifecycleQueryInstalledCC(resmgmt.WithTargets(peer))if err != nil {return flag, fmt.Errorf("LifecycleQueryInstalledCC error: %v", err)}for _, t := range resp1 {if t.PackageID == packageID { flag = true}}return flag, nil
}
获取已安装链码包
func getInstalledCCPackage(packageID string, org *OrgInfo) error {// use org1orgPeers, err := DiscoverLocalPeers(*org.OrgAdminClientContext, 1)if err != nil {return fmt.Errorf("DiscoverLocalPeers error: %v", err)}// 使用resmgmt中的LifecycleGetInstalledCCPackage方法,对于给定的packageID检索已安装的链码包if _, err := org.OrgResMgmt.LifecycleGetInstalledCCPackage(packageID, resmgmt.WithTargets([]fab.Peer{orgPeers[0]}...)); err != nil {return fmt.Errorf("LifecycleGetInstalledCCPackage error: %v", err)}return nil
}
查询安装
func queryInstalled(packageID string, org *OrgInfo) error {orgPeers, err := DiscoverLocalPeers(*org.OrgAdminClientContext, 1)if err != nil {return fmt.Errorf("DiscoverLocalPeers error: %v", err)}// 使用resmgmt中的LifecycleQueryInstalledCC方法,返回在指定节点上安装的链码packageIDresp1, err := org.OrgResMgmt.LifecycleQueryInstalledCC(resmgmt.WithTargets([]fab.Peer{orgPeers[0]}...))if err != nil {return fmt.Errorf("LifecycleQueryInstalledCC error: %v", err)}packageID1 := ""for _, t := range resp1 {if t.PackageID == packageID {packageID1 = t.PackageID}}// 查询的packageID与给定的packageID不一致则报错if !strings.EqualFold(packageID, packageID1) {return fmt.Errorf("check package id error")}return nil
}
各组织批准链码
func approveCC(packageID string, ccName, ccVersion string, sequence int64, channelID string, orgs []*OrgInfo, ordererEndpoint string) error {mspIDs := []string{}// 获取各个组织的mspIDfor _, org := range orgs {mspIDs = append(mspIDs, org.OrgMspId)}// 签名策略,由所有给出的mspid签名ccPolicy := policydsl.SignedByNOutOfGivenRole(int32(len(mspIDs)), mb.MSPRole_MEMBER, mspIDs)// approve所需参数approveCCReq := resmgmt.LifecycleApproveCCRequest{Name:              ccName,      // 链码名Version:           ccVersion,   // 版本PackageID:         packageID,   // 链码包idSequence:          sequence,    // 序列号EndorsementPlugin: "escc",      // 系统内置链码esccValidationPlugin:  "vscc",      // 系统内置链码vsccSignaturePolicy:   ccPolicy,    // 组织签名策略InitRequired:      true,        // 是否初始化}for _, org := range orgs{orgPeers, err := DiscoverLocalPeers(*org.OrgAdminClientContext, org.OrgPeerNum)fmt.Printf(">>> chaincode approved by %s peers:\n", org.OrgName)for _, p := range orgPeers {fmt.Printf("	%s\n", p.URL())}if err!=nil{return fmt.Errorf("DiscoverLocalPeers error: %v", err)}// 使用resmgmt中的LifecycleApproveCC方法为组织批准链码if _, err := org.OrgResMgmt.LifecycleApproveCC(channelID, approveCCReq, resmgmt.WithTargets(orgPeers...), resmgmt.WithOrdererEndpoint(ordererEndpoint), resmgmt.WithRetry(retry.DefaultResMgmtOpts));err != nil {fmt.Errorf("LifecycleApproveCC error: %v", err)}}return nil
}
查询已批准的链码
func queryApprovedCC(ccName string, sequence int64, channelID string, orgs []*OrgInfo) error {// queryApproved所需参数queryApprovedCCReq := resmgmt.LifecycleQueryApprovedCCRequest{Name:     ccName,  // 链码名称Sequence: sequence,// 序列号}for _, org := range orgs{orgPeers, err := DiscoverLocalPeers(*org.OrgAdminClientContext, org.OrgPeerNum)if err!=nil{return fmt.Errorf("DiscoverLocalPeers error: %v", err)}// Query approve ccfor _, p := range orgPeers {resp, err := retry.NewInvoker(retry.New(retry.TestRetryOpts)).Invoke(func() (interface{}, error) {// LifecycleQueryApprovedCC返回有关已批准的链码定义的信息resp1, err := org.OrgResMgmt.LifecycleQueryApprovedCC(channelID, queryApprovedCCReq, resmgmt.WithTargets(p))if err != nil {return nil, status.New(status.TestStatus, status.GenericTransient.ToInt32(), fmt.Sprintf("LifecycleQueryApprovedCC returned error: %v", err), nil)}return resp1, err},)if err != nil {return fmt.Errorf("Org %s Peer %s NewInvoker error: %v", org.OrgName, p.URL(), err)}if resp==nil{return fmt.Errorf("Org %s Peer %s Got nil invoker", org.OrgName, p.URL())}}}return nil
}
检查智能合约是否就绪
func checkCCCommitReadiness(packageID string, ccName, ccVersion string, sequence int64, channelID string, orgs []*OrgInfo) error {mspIds := []string{}for _, org := range orgs {mspIds = append(mspIds, org.OrgMspId)}// 签名策略,由所有给出的mspid签名ccPolicy := policydsl.SignedByNOutOfGivenRole(int32(len(mspIds)), mb.MSPRole_MEMBER, mspIds)// 所需所有参数,同上req := resmgmt.LifecycleCheckCCCommitReadinessRequest{Name:              ccName,Version:           ccVersion,//PackageID:         packageID,EndorsementPlugin: "escc",ValidationPlugin:  "vscc",SignaturePolicy:   ccPolicy,Sequence:          sequence,InitRequired:      true,}for _, org := range orgs{orgPeers, err := DiscoverLocalPeers(*org.OrgAdminClientContext, org.OrgPeerNum)if err!=nil{fmt.Errorf("DiscoverLocalPeers error: %v", err)}for _, p := range orgPeers {resp, err := retry.NewInvoker(retry.New(retry.TestRetryOpts)).Invoke(func() (interface{}, error) {// 使用resmgmt中的LifecycleCheckCCCommitReadiness方法检查链代码的“提交准备”,返回组织批准。resp1, err := org.OrgResMgmt.LifecycleCheckCCCommitReadiness(channelID, req, resmgmt.WithTargets(p))fmt.Printf("LifecycleCheckCCCommitReadiness cc = %v, = %v\n", ccName, resp1)if err != nil {return nil, status.New(status.TestStatus, status.GenericTransient.ToInt32(), fmt.Sprintf("LifecycleCheckCCCommitReadiness returned error: %v", err), nil)}flag := truefor _, r := range resp1.Approvals {flag = flag && r}if !flag {return nil, status.New(status.TestStatus, status.GenericTransient.ToInt32(), fmt.Sprintf("LifecycleCheckCCCommitReadiness returned : %v", resp1), nil)}return resp1, err},)if err != nil {return fmt.Errorf("NewInvoker error: %v", err)}if resp==nil{return fmt.Errorf("Got nill invoker response")}}}return nil
}
提交智能合约定义

一旦足够多的通道成员同意一个链码定义,某个组织能够提交定义到通道。你可以用上述 checkcommitreadiness 方法在将链码定义提交到通道之前,基于哪个通道成员已经批准了该定义,来检查提交链码定义是否应该成功。(根据通道成员同意的状况,来判断提交是否可能成功)。提交交易请求首先发送给通道成员的 peer节点,peer节点会查询链码定义被他们组织同意的状况,并且为定义背书如果所在组织已经同意了。交易然后提交给排序服务,排序服务会把链码定义提交给通道。提交定义交易需要以 Organization Administrator 身份来提交。
链码在被成功提交到通道之前,需要被同意的组织的数量是通过 Channel/Application/LifecycleEndorsement 策略来管理的。默认情况下,这个策略需要通道中大多数的组织来给交易背书。生命周期背书策略不同于链码背书策略。例如,尽管一个链码背书策略只需要一个或两个组织的签名,根据默认策略大多数的通道成员仍然需要批准链码定义。当提交一个通道定义,你需要面向足够多的 peer 组织,以确保你的生命周期背书策略被满足。
你也可以设置 Channel/Application/LifecycleEndorsement 策略为一个签名策略并且明确指明通道上可以批准链码定义的组织集合。这允许你创建一个其中大多数组织作为链码管理者并且治理通道业务逻辑的通道。如果你的通道有大量的Idemix(身份混合,实现零知识证明)组织,你也可以用一个签名策略(策略只需要一个签名),因为这些组织不能批准链码定义或者为链码背书并且可能阻碍通道达成大多数成员同意的结果。
一个组织在不安装链码包的条件下能够批准链码定义。如果一个组织不需要使用链码,他们可以在没有包身份的情况下批准一个链码定义来确保生命周期背书策略被满足。
在链码定义已经提交到通道上后,链码容器会在所有的链码安装到的 peer 节点上启动,来允许通道成员开始使用链码。可能会花费几分钟的时间来启动链码容器。你可以用链码定义来要求调用 Init 方法初始化链码。如果 Init 方法调用是需要的,链码的第一个调用必须是调用 Init 方法。Init 方法的调用服从于链码的背书策略。

func commitCC(ccName, ccVersion string, sequence int64, channelID string, orgs []*OrgInfo, ordererEndpoint string) error{mspIDs := []string{}for _, org := range orgs {mspIDs = append(mspIDs, org.OrgMspId)}ccPolicy := policydsl.SignedByNOutOfGivenRole(int32(len(mspIDs)), mb.MSPRole_MEMBER, mspIDs)// commit所需参数信息,内容同上req := resmgmt.LifecycleCommitCCRequest{Name:              ccName,Version:           ccVersion,Sequence:          sequence,EndorsementPlugin: "escc",ValidationPlugin:  "vscc",SignaturePolicy:   ccPolicy,InitRequired:      true,}// LifecycleCommitCC将链代码提交给给定的通道_, err := orgs[0].OrgResMgmt.LifecycleCommitCC(channelID, req, resmgmt.WithOrdererEndpoint(ordererEndpoint), resmgmt.WithRetry(retry.DefaultResMgmtOpts))if err != nil {return fmt.Errorf("LifecycleCommitCC error: %v", err)}return nil
}
查询已提交的智能合约定义
func queryCommittedCC( ccName string, channelID string, sequence int64, orgs []*OrgInfo) error {req := resmgmt.LifecycleQueryCommittedCCRequest{Name: ccName,}for _, org := range orgs {orgPeers, err := DiscoverLocalPeers(*org.OrgAdminClientContext, org.OrgPeerNum)if err!=nil{return fmt.Errorf("DiscoverLocalPeers error: %v", err)}for _, p := range orgPeers {resp, err := retry.NewInvoker(retry.New(retry.TestRetryOpts)).Invoke(func() (interface{}, error) {// LifecycleQueryCommittedCC查询给定通道上提交的链码resp1, err := org.OrgResMgmt.LifecycleQueryCommittedCC(channelID, req, resmgmt.WithTargets(p))if err != nil {return nil, status.New(status.TestStatus, status.GenericTransient.ToInt32(), fmt.Sprintf("LifecycleQueryCommittedCC returned error: %v", err), nil)}flag := falsefor _, r := range resp1 {if r.Name == ccName && r.Sequence == sequence {flag = truebreak}}if !flag {return nil, status.New(status.TestStatus, status.GenericTransient.ToInt32(), fmt.Sprintf("LifecycleQueryCommittedCC returned : %v", resp1), nil)}return resp1, err},)if err != nil {return  fmt.Errorf("NewInvoker error: %v", err)}if resp==nil{return fmt.Errorf("Got nil invoker response")}}}return nil
}
智能合约初始化
func initCC(ccName string, upgrade bool, channelID string, org *OrgInfo, sdk *fabsdk.FabricSDK) error {// 准备通道客户端上下文clientChannelContext := sdk.ChannelContext(channelID, fabsdk.WithUser(org.OrgUser), fabsdk.WithOrg(org.OrgName))// 通道客户端用于查询执行交易client, err := channel.New(clientChannelContext)if err != nil {return fmt.Errorf("Failed to create new channel client: %s", err)}// 调用链码初始化_, err = client.Execute(channel.Request{ChaincodeID: ccName, Fcn: "init", Args: nil, IsInit: true},channel.WithRetry(retry.DefaultChannelOpts))if err != nil {return fmt.Errorf("Failed to init: %s", err)}return nil
}
调用创建通道函数及加入通道
func CreateAndJoinChannel(info *SdkEnvInfo) error {fmt.Println(">> 开始创建通道......")if len(info.Orgs) == 0 {return fmt.Errorf("通道组织不能为空,请提供组织信息")}// 获得所有组织的签名信息signIds := []msp.SigningIdentity{}for _, org := range info.Orgs {// Get signing identity that is used to sign create channel requestorgSignId, err := org.orgMspClient.GetSigningIdentity(org.OrgAdminUser)if err != nil {return fmt.Errorf("GetSigningIdentity error: %v", err)}signIds = append(signIds, orgSignId)}// 创建通道,createChannel方法在下面定义if err := createChannel(signIds, info); err != nil {return fmt.Errorf("Create channel error: %v", err)}fmt.Println(">> 创建通道成功")fmt.Println(">> 加入通道......")for _, org := range info.Orgs {// 加入通道if err := org.OrgResMgmt.JoinChannel(info.ChannelID, resmgmt.WithRetry(retry.DefaultResMgmtOpts), resmgmt.WithOrdererEndpoint("orderer.example.com")); err != nil {return fmt.Errorf("%s peers failed to JoinChannel: %v", org.OrgName, err)}}fmt.Println(">> 加入通道成功")return nil
}
创建通道
func createChannel(signIDs []msp.SigningIdentity, info *SdkEnvInfo) error {// Channel management client 负责管理通道,如创建更新通道chMgmtClient, err := resmgmt.New(*info.OrdererClientContext)if err != nil {return fmt.Errorf("Channel management client create error: %v", err)}// 根据channel.tx创建通道req := resmgmt.SaveChannelRequest{ChannelID: info.ChannelID,ChannelConfigPath: info.ChannelConfig,SigningIdentities: signIDs}if _, err := chMgmtClient.SaveChannel(req, resmgmt.WithRetry(retry.DefaultResMgmtOpts), resmgmt.WithOrdererEndpoint("orderer.example.com")); err != nil {return fmt.Errorf("error should be nil for SaveChannel of orgchannel: %v", err)}fmt.Println(">>>> 使用每个org的管理员身份更新锚节点配置...")//根据锚节点文件更新锚节点,与上面创建通道流程相同for i, org := range info.Orgs {req = resmgmt.SaveChannelRequest{ChannelID: info.ChannelID,ChannelConfigPath: org.OrgAnchorFile,SigningIdentities: []msp.SigningIdentity{signIDs[i]}}if _, err = org.OrgResMgmt.SaveChannel(req, resmgmt.WithRetry(retry.DefaultResMgmtOpts), resmgmt.WithOrdererEndpoint("orderer.example.com")); err != nil {return fmt.Errorf("SaveChannel for anchor org %s error: %v", org.OrgName, err)}}fmt.Println(">>>> 使用每个org的管理员身份更新锚节点配置完成")return nil
}
智能合约完整生命周期
func CreateCCLifecycle(info *SdkEnvInfo, sequence int64, upgrade bool, sdk *fabsdk.FabricSDK) error {if len(info.Orgs) == 0 {return fmt.Errorf("the number of organization should not be zero.")}// 打包链码fmt.Println(">> 开始打包链码......")label, ccPkg, err := packageCC(info.ChaincodeID, info.ChaincodeVersion, info.ChaincodePath)if err != nil {return fmt.Errorf("pakcagecc error: %v", err)}packageID := lcpackager.ComputePackageID(label, ccPkg)fmt.Println(">> 打包链码成功")// 安装链码fmt.Println(">> 开始安装链码......")if err := installCC(label, ccPkg, info.Orgs); err != nil {return fmt.Errorf("installCC error: %v", err)}// 检索已安装链码包if err := getInstalledCCPackage(packageID, info.Orgs[0]); err != nil {return fmt.Errorf("getInstalledCCPackage error: %v", err)}// 查询已安装链码if err := queryInstalled(packageID, info.Orgs[0]); err != nil {return fmt.Errorf("queryInstalled error: %v", err)}fmt.Println(">> 安装链码成功")// 批准链码fmt.Println(">> 组织认可智能合约定义......")if err := approveCC(packageID, info.ChaincodeID, info.ChaincodeVersion, sequence, info.ChannelID, info.Orgs, info.OrdererEndpoint); err != nil {return fmt.Errorf("approveCC error: %v", err)}// 查询批准if err:=queryApprovedCC(info.ChaincodeID, sequence, info.ChannelID, info.Orgs);err!=nil{return fmt.Errorf("queryApprovedCC error: %v", err)}fmt.Println(">> 组织认可智能合约定义完成")// 检查智能合约是否就绪fmt.Println(">> 检查智能合约是否就绪......")if err:=checkCCCommitReadiness(packageID, info.ChaincodeID, info.ChaincodeVersion, sequence, info.ChannelID, info.Orgs); err!=nil{return fmt.Errorf("checkCCCommitReadiness error: %v", err)}fmt.Println(">> 智能合约已经就绪")// Commitfmt.Println(">> 提交智能合约定义......")if err:=commitCC(info.ChaincodeID, info.ChaincodeVersion, sequence, info.ChannelID, info.Orgs, info.OrdererEndpoint);err!=nil{return fmt.Errorf("commitCC error: %v", err)}// 查询Commit结果if err:=queryCommittedCC(info.ChaincodeID, info.ChannelID, sequence, info.Orgs); err!=nil{return fmt.Errorf("queryCommittedCC error: %v", err)}fmt.Println(">> 智能合约定义提交完成")// 初始化fmt.Println(">> 调用智能合约初始化方法......")if err:=initCC(info.ChaincodeID, upgrade, info.ChannelID, info.Orgs[0], sdk); err!=nil{return fmt.Errorf("initCC error: %v", err)}fmt.Println(">> 完成智能合约初始化")return nil
}

set

package sdkInitimport ("github.com/hyperledger/fabric-sdk-go/pkg/client/channel"
)func (t *Application) Set(args []string) (string, error) {var tempArgs [][]bytefor i := 1; i < len(args); i++ {tempArgs = append(tempArgs, []byte(args[i]))}request := channel.Request{ChaincodeID: t.SdkEnvInfo.ChaincodeID, Fcn: args[0], Args: [][]byte{[]byte(args[1]), []byte(args[2])}}response, err := t.SdkEnvInfo.ChClient.Execute(request)if err != nil {// 资产转移失败return "", err}//fmt.Println("============== response:",response)return string(response.TransactionID), nil
}

相关内容

热门资讯

抗战影像记忆|这些影像,让世界...   1939年7月,美国《生活》(LIFE)杂志刊发了一组珍贵的新四军抗战影像,向世界展现了中国共产...
三种颜色,解码机遇中国   一片绿、一抹白、一把红,如何展现一个充满生机的贵州?  7月15日至18日,2025“机遇中国 ...
11.7%、5000亿元、8....   央视网消息:记者从商务部了解到,今年前6个月,全国新设立外商投资企业30014家,同比增长11....
“链通”创未来丨“对各方来说这...   以“链接世界、共创未来”为主题的第三届中国国际供应链促进博览会(链博会)7月16日至20日在北京...
舌尖上的消暑佳品唤醒盛夏记忆 ...   央视网消息:闷热天气里,最适合吃点清爽开胃的东西。我们国家幅员辽阔,每个地方都有自己独特的夏日美...
铭记历史 缅怀先烈|萨师俊:誓...   在福州市中心的安泰河畔,静谧祥和的巷弄里,朱紫坊22号萨家大院饱经沧桑。叩开红漆大门,挂在厅堂里...
青峰翠壑、奇松怪石、碧海银沙…...   央视网消息:盛夏时节,一起来感受大美中国。近日,在安徽黄山风景区,一场大雨之后,突现彩虹美景。雨...
上半年国家铁路发送货物19.8...   新华社北京7月20日电(记者樊曦)记者20日从中国国家铁路集团有限公司获悉,今年上半年,国家铁路...
人民网评:以工匠精神照亮中小企...   中小企业联系千家万户,是推动创新、促进就业、改善民生的重要力量。日前,全国总工会、工业和信息化部...
人民财评:链博会逐“绿”向“新...   世界500强和行业龙头企业占比超过65%,首发首秀首展超过100项……第三届中国国际供应链促进博...