[2021]fabric系列五:Fabric手动组件网络教程(2)
关键步骤:
- 将每个组织的每个节点都加入到通道中 - > 客户端来完成
- 一个客户端只能同时连接一个peer节点
- 给每个peer节点安装智能合约 -> 链代码(程序:go,node.js,java)
- 对智能合约进行初始化,对应智能合约中的Init函数
- 只需要在任意节点初始化一次,数据会自动同步到各个组织的各个节点
- 对数据进行查询 -> 读
- 对数据进行调用 -> 写
1 ChainCode
1.1 Golang版本的Chaincode的代码结构
- 链代码的包名的指定
// xxx.go
package main
- 必须要引入的包
// go get github.com/hyperledger/fabric/core/chaincode/shim
import {
//客户端和Fabric框架通信
"github.com/hyperledger/fabric/core/chaincode/shim"
//起一个别名
pb "github.com/hyperledger/fabric/protos/peer"
}
- 链码的书写要求
//自定义一个结构体,基于这个类实现一些接口函数
type Test struct{
// 空着即可
}
func (t* Test) Init(stub ChaincodeStubInterface) pb.Response
func (t* Test) Invoke(stub ChaincodeStubInterface) pb.Response
- 链码API查询
链码查询
1.2 shim包常用函数介绍
1.3 举例
// chaincode/go/test1/test.go -> 对应的链码名testcc
package main
import {
}
type Test struct{
}
func (t* Test)Init();
func (t* Test)Invoke();
func main(){
}
// chaincode/go/test2/test2.go -> 对应的链码名testcc1
package main
import {
}
type Test struct{
}
func (t* Test)Init();
func (t* Test)Invoke();
func main(){
}
// 安装流程
- 安装test1目录中的链码
- 安装test2目录中的链码
1.4 ChainCode交易的背书
# 指定背书策略
peer chaincode instantiate -o orderer.tgy.com:7050 -C mychannel -n testcc -l golang -v 1.0 -c '{"Args":["init","a","100","b","200"]}' -P "AND ('OrgGoMSP.member','OrgCppMSP.member')"
# 调用
# 调用示例
$ peer chaincode instantiate -o orderer.tgy.com:7050 -C mychannel -n testcc --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/tgy.com/msp/tlscacerts/tlsca.tgy.com-cert.pem --peerAddresses peer0.orgGo.tgy.com:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/orgGo.tgy.com/peers/peer0.orgGo.tgy.com/tls/ca.crt -peerAddresses peer0.orgCpp.tgy.com:9051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/orgGo.tgy.com/peers/peer0.orgCpp.tgy.com/tls/ca.crt -c '{"Args":["invoke","a","b","10"]}'
1.5 示例链码
package main
import (
"fmt"
"strconv"
"github.com/hyperledger/fabric/core/chaincode/shim"
pb "github.com/hyperledger/fabric/protos/peer"
)
// SimpleChaincode example simple Chaincode implementation
type SimpleChaincode struct {
}
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
fmt.Println("ex02 Init")
_, args := stub.GetFunctionAndParameters()
var A, B string // Entities
var Aval, Bval int // Asset holdings
var err error
if len(args) != 4 {
return shim.Error("Incorrect number of arguments. Expecting 4")
}
// Initialize the chaincode
A = args[0]
Aval, err = strconv.Atoi(args[1])
if err != nil {
return shim.Error("Expecting integer value for asset holding")
}
B = args[2]
Bval, err = strconv.Atoi(args[3])
if err != nil {
return shim.Error("Expecting integer value for asset holding")
}
fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval)
// Write the state to the ledger
err = stub.PutState(A, []byte(strconv.Itoa(Aval)))
if err != nil {
return shim.Error(err.Error())
}
err = stub.PutState(B, []byte(strconv.Itoa(Bval)))
if err != nil {
return shim.Error(err.Error())
}
return shim.Success(nil)
}
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
fmt.Println("ex02 Invoke")
function, args := stub.GetFunctionAndParameters()
if function == "invoke" {
// Make payment of X units from A to B
return t.invoke(stub, args)
} else if function == "delete" {
// Deletes an entity from its state
return t.delete(stub, args)
} else if function == "query" {
// the old "Query" is now implemtned in invoke
return t.query(stub, args)
}
return shim.Error("Invalid invoke function name. Expecting \"invoke\" \"delete\" \"query\"")
}
// Transaction makes payment of X units from A to B
func (t *SimpleChaincode) invoke(stub shim.ChaincodeStubInterface, args []string) pb.Response {
var A, B string // Entities
var Aval, Bval int // Asset holdings
var X int // Transaction value
var err error
if len(args) != 3 {
return shim.Error("Incorrect number of arguments. Expecting 3")
}
A = args[0]
B = args[1]
// Get the state from the ledger
// TODO: will be nice to have a GetAllState call to ledger
Avalbytes, err := stub.GetState(A)
if err != nil {
return shim.Error("Failed to get state")
}
if Avalbytes == nil {
return shim.Error("Entity not found")
}
Aval, _ = strconv.Atoi(string(Avalbytes))
Bvalbytes, err := stub.GetState(B)
if err != nil {
return shim.Error("Failed to get state")
}
if Bvalbytes == nil {
return shim.Error("Entity not found")
}
Bval, _ = strconv.Atoi(string(Bvalbytes))
// Perform the execution
X, err = strconv.Atoi(args[2])
if err != nil {
return shim.Error("Invalid transaction amount, expecting a integer value")
}
Aval = Aval - X
Bval = Bval + X
fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval)
// Write the state back to the ledger
err = stub.PutState(A, []byte(strconv.Itoa(Aval)))
if err != nil {
return shim.Error(err.Error())
}
err = stub.PutState(B, []byte(strconv.Itoa(Bval)))
if err != nil {
return shim.Error(err.Error())
}
return shim.Success(nil)
}
// Deletes an entity from state
func (t *SimpleChaincode) delete(stub shim.ChaincodeStubInterface, args []string) pb.Response {
if len(args) != 1 {
return shim.Error("Incorrect number of arguments. Expecting 1")
}
A := args[0]
// Delete the key from the state in ledger
err := stub.DelState(A)
if err != nil {
return shim.Error("Failed to delete state")
}
return shim.Success(nil)
}
// query callback representing the query of a chaincode
func (t *SimpleChaincode) query(stub shim.ChaincodeStubInterface, args []string) pb.Response {
var A string // Entities
var err error
if len(args) != 1 {
return shim.Error("Incorrect number of arguments. Expecting name of the person to query")
}
A = args[0]
// Get the state from the ledger
Avalbytes, err := stub.GetState(A)
if err != nil {
jsonResp := "{\"Error\":\"Failed to get state for " + A + "\"}"
return shim.Error(jsonResp)
}
if Avalbytes == nil {
jsonResp := "{\"Error\":\"Nil amount for " + A + "\"}"
return shim.Error(jsonResp)
}
jsonResp := "{\"Name\":\"" + A + "\",\"Amount\":\"" + string(Avalbytes) + "\"}"
fmt.Printf("Query Response:%s\n", jsonResp)
return shim.Success(Avalbytes)
}
func main() {
err := shim.Start(new(SimpleChaincode))
if err != nil {
fmt.Printf("Error starting Simple chaincode: %s", err)
}
}
main函数编写:
func main() {
err := shim.Start(new(SimpleChaincode))
if err != nil {
fmt.Printf("Error starting Simple chaincode: %s", err)
}
}
// 如果要自定义函数,函数格式:
func (t* xxx) funcName(stub shim.ChaincodeStubInterface, args []string) pb.Response {}
2 Fabric账号
Fabric账号解析
docker-compose.yaml配置项的修改:
server:
ca.tgy.com:
image: hyperledger/fabric-ca:latest
environment:
- FABRIC_CA_HOME=/etc/hyperledger/fabric-ca-server
- FABRIC_CA_SERVER_CA_NAME=ca.tgy.com
- FABRIC_CA_SERVER_CA_CERTFILE=/etc/hyperledger/fabric-ca-server-config/ca.orgGo.tgy.com-cert.pem
- FABRIC_CA_SERVER_CA_KEYFILE=/etc/hyperledger/fabric-ca-server-config/f735cc823f3ee2d042988278fcde094ea0b1a7278b235a4701ce503dc25dd97a_sk
ports:
- "7054:7054"
command: sh -c 'fabric-ca-server start -b admin:123456'
volumes:
- ./crypto-config/peerOrganizations/orgGo.tgy.com/ca/:/etc/hyperledger/fabric-ca-server-config
container_name: ca.tgy.com
networks:
- byfn
- 客户端参考API
https://fabric-sdk-node.github.io/release-1.3/index.html