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
点击上次作业链接,查看三种实例的运行结果
/*
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)}
}
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
}
/*
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
}
/*
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运行实战,这是之前实验的内容,在此不多赘述。
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) 协议
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"
)
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
}
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"
)
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
}
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
}
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
}
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
}
上一篇:多国宣布:暂停援助!