1. 程式人生 > >部署測試fabric1.0及原始碼解析

部署測試fabric1.0及原始碼解析

開發環境

  • UBUNTU 16.04 LTS
  • docker
  • docker-compose
  • git
  • go 1.8以上

docker,docker-compose以及go的安裝這裡不再描述。

部署測試

新建fabric-sample目錄(名字可以任意),進入目錄執行:

curl -sSL https://raw.githubusercontent.com/hyperledger/fabric/master/scripts/bootstrap-1.0.0-beta.sh | bash

這個指令碼會下載需要的docker映象以及自動化指令碼,執行完畢後,首先會在當前目錄看到一個release的資料夾,裡面有fabric執行環境的啟動指令碼。

其次,我們需要的docker映象也會一併拉取過來,如下:

hyperledger/fabric-tools                 latest                ae6b0f53cb70        5 months ago        1.32GB
hyperledger/fabric-tools                 x86_64-1.0.0-beta     ae6b0f53cb70        5 months ago        1.32GB
hyperledger/fabric-couchdb               latest                31
bbbec3d853 5 months ago 1.48GB hyperledger/fabric-couchdb x86_64-1.0.0-beta 31bbbec3d853 5 months ago 1.48GB hyperledger/fabric-kafka latest c4ac1c9a4797 5 months ago 1.3GB hyperledger/fabric-kafka x86_64-
1.0.0-beta c4ac1c9a4797 5 months ago 1.3GB hyperledger/fabric-zookeeper latest 2c4ebacb6f00 5 months ago 1.31GB hyperledger/fabric-zookeeper x86_64-1.0.0-beta 2c4ebacb6f00 5 months ago 1.31GB hyperledger/fabric-orderer latest 11ff350dd297 5 months ago 179MB hyperledger/fabric-orderer x86_64-1.0.0-beta 11ff350dd297 5 months ago 179MB hyperledger/fabric-peer latest e01c2b645f11 5 months ago 182MB hyperledger/fabric-peer x86_64-1.0.0-beta e01c2b645f11 5 months ago 182MB hyperledger/fabric-javaenv latest 61c188dca542 5 months ago 1.42GB hyperledger/fabric-javaenv x86_64-1.0.0-beta 61c188dca542 5 months ago 1.42GB hyperledger/fabric-ccenv latest 7034cca1918d 5 months ago 1.29GB hyperledger/fabric-ccenv x86_64-1.0.0-beta 7034cca1918d 5 months ago 1.29GB hyperledger/fabric-ca latest e549e8c53c2e 5 months ago 238MB hyperledger/fabric-ca x86_64-1.0.0-beta e549e8c53c2e 5 months ago 238MB hyperledger/fabric-ccenv x86_64-1.0.0-alpha2 8c360a57f805 5 months ago 1.29GB hyperledger/fabric-baseos x86_64-0.3.1 4b0cab202084 6 months ago 157MB hyperledger/fabric-baseos x86_64-0.3.2 4b0cab202084 6 months ago 157MB

這裡有一點要注意,如果你的環境之前執行過其它的fabric測試,可能會存在其它版本的docker映象,建議刪除。我試過不刪除結果後面各種錯誤,刪除之後一切正常。

啟動fabric

cd ~/fabric-sample/release/linux-amd64
./network_setup.sh up

注意看有沒有報錯,正常的話最後會有下面這樣的介面。

Query Result: 90
2017-11-10 03:48:00.802 UTC [main] main -> INFO 008 Exiting.....
===================== Query on PEER3 on channel 'mychannel' is successful ===================== 

===================== All GOOD, End-2-End execution completed ===================== 


 _____   _   _   ____            _____   ____    _____ 
| ____| | \ | | |  _ \          | ____| |___ \  | ____|
|  _|   |  \| | | | | |  _____  |  _|     __) | |  _|  
| |___  | |\  | | |_| | |_____| | |___   / __/  | |___ 
|_____| |_| \_| |____/          |_____| |_____| |_____|

啟動成功後,我們用docker ps命令可以看到類似下面這樣的資訊,

[email protected]e:~/gopath/src/fabric-sample# docker ps
CONTAINER ID        IMAGE                                    COMMAND                  CREATED             STATUS              PORTS                                              NAMES
e1f67091b0be        dev-peer1.org2.example.com-mycc-1.0      "chaincode -peer.a..."   2 hours ago         Up 2 hours                                                             dev-peer1.org2.example.com-mycc-1.0
c1c82b8269d1        dev-peer0.org1.example.com-mycc-1.0      "chaincode -peer.a..."   2 hours ago         Up 2 hours                                                             dev-peer0.org1.example.com-mycc-1.0
5610d9ef3dfa        dev-peer0.org2.example.com-mycc-1.0      "chaincode -peer.a..."   2 hours ago         Up 2 hours                                                             dev-peer0.org2.example.com-mycc-1.0
e5fee400df40        hyperledger/fabric-tools                 "/bin/bash -c './s..."   2 hours ago         Up 2 hours                                                             cli
12252e8c234e        hyperledger/fabric-peer                  "peer node start"        2 hours ago         Up 2 hours          0.0.0.0:8051->7051/tcp, 0.0.0.0:8053->7053/tcp     peer1.org1.example.com
82c82528a0e3        hyperledger/fabric-orderer               "orderer"                2 hours ago         Up 2 hours          0.0.0.0:7050->7050/tcp                             orderer.example.com
8e9ed0104cc9        hyperledger/fabric-peer                  "peer node start"        2 hours ago         Up 2 hours          0.0.0.0:7051->7051/tcp, 0.0.0.0:7053->7053/tcp     peer0.org1.example.com
6475f787dd66        hyperledger/fabric-peer                  "peer node start"        2 hours ago         Up 2 hours          0.0.0.0:10051->7051/tcp, 0.0.0.0:10053->7053/tcp   peer1.org2.example.com
2c65a798dca2        hyperledger/fabric-peer                  "peer node start"        2 hours ago         Up 2 hours          0.0.0.0:9051->7051/tcp, 0.0.0.0:9053->7053/tcp     peer0.org2.example.com

也就是說,剛才的指令碼為我們啟動了一個cli客戶端,一個orderer節點,4個普通的peer節點。而且,還有三個chaincode執行例項。

新開啟一個終端,進入cli容器,

docker exec -it cli bash

下面我們首先安裝Example02,並指定一個名字,比如我們這裡就用devincc:

peer chaincode install -n devincc -v 1.0 -p github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02

這裡安裝指定的目錄是docker容器中的gopath路徑下的
github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02
目錄中的chaincode程式。

下面有個專門的章節我會分析下這部分程式碼。

執行後可以看到提示執行成功,類似如下的資訊:

2017-11-10 03:50:29.907 UTC [golang-platform] GetDeploymentPayload -> DEBU 00c done
2017-11-10 03:50:29.909 UTC [msp/identity] Sign -> DEBU 00d Sign: plaintext: 0AA6070A5C08031A0C0885C494D00510...98F1CF000000FFFF6430F69C001C0000 
2017-11-10 03:50:29.909 UTC [msp/identity] Sign -> DEBU 00e Sign: digest: 5779AC7B30E7CFBAA5E698B90762616117CA8FA36144238F83C6A4A047D7A737 
2017-11-10 03:50:29.914 UTC [chaincodeCmd] install -> DEBU 00f Installed remotely response:<status:200 payload:"OK" > 
....

初始化例項,設定a賬戶有100元,b賬戶有200元。

root@e5fee400df40:/opt/gopath/src/github.com/hyperledger/fabric/peer# peer chaincode instantiate -o orderer.example.com:7050 --tls $CORE_PEER_TLS_ENABLED --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/cacerts/ca.example.com-cert.pem -C mychannel -n devincc -v 1.0 -c '{"Args":["init","a", "100", "b","200"]}' -P "OR ('Org1MSP.member','Org2MSP.member')"

注意看有沒有報錯。

用Query命令來看一看a賬戶的餘額:

[email protected]:/opt/gopath/src/github.com/hyperledger/fabric/peer# peer chaincode query -C mychannel -n devincc -c '{"Args":["query","a"]}'
2017-11-10 03:53:25.599 UTC [msp] getMspConfig -> INFO 001 intermediate certs folder not found at [/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/[email protected].example.com/msp/intermediatecerts]. Skipping.: [stat /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/[email protected].example.com/msp/intermediatecerts: no such file or directory]
2017-11-10 03:53:25.599 UTC [msp] getMspConfig -> INFO 002 crls folder not found at [/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/[email protected].example.com/msp/intermediatecerts]. Skipping.: [stat /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/[email protected].example.com/msp/crls: no such file or directory]
2017-11-10 03:53:25.599 UTC [msp] getMspConfig -> INFO 003 MSP configuration file not found at [/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/[email protected].example.com/msp/config.yaml]: [stat /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/[email protected].example.com/msp/config.yaml: no such file or directory]
2017-11-10 03:53:25.623 UTC [msp] GetLocalMSP -> DEBU 004 Returning existing local MSP
2017-11-10 03:53:25.623 UTC [msp] GetDefaultSigningIdentity -> DEBU 005 Obtaining default signing identity
2017-11-10 03:53:25.623 UTC [msp/identity] Sign -> DEBU 006 Sign: plaintext: 0AB4070A6A08031A0C08B5C594D00510...696E63631A0A0A0571756572790A0161 
2017-11-10 03:53:25.623 UTC [msp/identity] Sign -> DEBU 007 Sign: digest: 00AA703102DBEA060C31B1E9FDD66143AD6454A27C2F1757ADE5265C992207A4 
Query Result: 100

餘額是100元。

接下來我們把a賬戶的10元轉給b賬戶,需要呼叫invoke命令:

[email protected]:/opt/gopath/src/github.com/hyperledger/fabric/peer# peer chaincode invoke -o orderer.example.com:7050  --tls $CORE_PEER_TLS_ENABLED --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/cacerts/ca.example.com-cert.pem  -C mychannel -n devincc -c '{"Args":["invoke","a","b","10"]}'
2017-11-10 03:54:06.834 UTC [msp] getMspConfig -> INFO 001 intermediate certs folder not found at [/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/[email protected].example.com/msp/intermediatecerts]. Skipping.: [stat /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/[email protected].example.com/msp/intermediatecerts: no such file or directory]
2017-11-10 03:54:06.834 UTC [msp] getMspConfig -> INFO 002 crls folder not found at [/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/[email protected].example.com/msp/intermediatecerts]. Skipping.: [stat /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/[email protected].example.com/msp/crls: no such file or directory]
2017-11-10 03:54:06.835 UTC [msp] getMspConfig -> INFO 003 MSP configuration file not found at [/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/[email protected].example.com/msp/config.yaml]: [stat /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/[email protected].example.com/msp/config.yaml: no such file or directory]
2017-11-10 03:54:06.882 UTC [msp] GetLocalMSP -> DEBU 004 Returning existing local MSP
2017-11-10 03:54:06.882 UTC [msp] GetDefaultSigningIdentity -> DEBU 005 Obtaining default signing identity
2017-11-10 03:54:06.887 UTC [msp/identity] Sign -> DEBU 006 Sign: plaintext: 0AB4070A6A08031A0C08DEC594D00510...696E766F6B650A01610A01620A023130 
2017-11-10 03:54:06.887 UTC [msp/identity] Sign -> DEBU 007 Sign: digest: CD26EE32A303A100BE43D3CCDDA6403FAA35F497ACA0C96F8C46A95A2487BDFD 
2017-11-10 03:54:06.902 UTC [msp/identity] Sign -> DEBU 008 Sign: plaintext: 0AB4070A6A08031A0C08DEC594D00510...82890A68992DCC72B079EC46631ECB78 
2017-11-10 03:54:06.902 UTC [msp/identity] Sign -> DEBU 009 Sign: digest: 51965C0B97EEB31A296977BA323972119BC4BE62EE5F18BB70B1EC69CFDF787B 
2017-11-10 03:54:06.920 UTC [chaincodeCmd] chaincodeInvokeOrQuery -> DEBU 00a ESCC invoke result: version:1 response:<status:200 message:"OK" > payload:"\n \213<\t\264i\241\247\235\257W\230\242W\236\251\\\2234\256\214\265\243?\236\274Z\200\025AU\377\026\022b\nK\0220\n\007devincc\022%\n\007\n\001a\022\002\010\005\n\007\n\001b\022\002\010\005\032\007\n\001a\032\00290\032\010\n\001b\032\003210\022\027\n\004lscc\022\017\n\r\n\007devincc\022\002\010\005\032\003\010\310\001\"\016\022\007devincc\032\0031.0" endorsement:<endorser:"\n\007Org1MSP\022\325\006-----BEGIN -----\nMIICWTCCAgCgAwIBAgIQahOTRu7gpuR5AtIhNk3n1zAKBggqhkjOPQQDAjBzMQsw\nCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy\nYW5jaXNjbzEZMBcGA1UEChMQb3JnMS5leGFtcGxlLmNvbTEcMBoGA1UEAxMTY2Eu\nb3JnMS5leGFtcGxlLmNvbTAeFw0xNzExMTAwMzQ2NDNaFw0yNzExMDgwMzQ2NDNa\nMFsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1T\nYW4gRnJhbmNpc2NvMR8wHQYDVQQDExZwZWVyMC5vcmcxLmV4YW1wbGUuY29tMFkw\nEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEVn40lbdv6epcF1bx37dS9nbGJdRVCAkG\n7hsUKHdvjOi9fAsU0fatanSZjtv2gJq/fXB/f3huHUijTP9H413zsaOBjTCBijAO\nBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIw\nADArBgNVHSMEJDAigCBKiLTrKKZzBXbm3oXivXoLjt8WfFszW8Ca76+pGi+wQTAo\nBgNVHREEITAfghZwZWVyMC5vcmcxLmV4YW1wbGUuY29tggVwZWVyMDAKBggqhkjO\nPQQDAgNHADBEAiA1uMkgn7nAb3uk3C8sQG0jBNHbY09eDKqb3R6e2uagcwIgJg6w\nybzss2TAuaxqWMYX+JutKEJTlxSThiYjMaHcJds=\n-----END -----\n" signature:"0E\002!\000\341\334cR>\343\273\221\005E#\344[\032\377Q5IR\323qNH\3778\025,\243\243o(W\002 \013\036\220\234V\271\2651\005\254\301p\016&\254\216\202\211\nh\231-\314r\260y\354Fc\036\313x" > 
2017-11-10 03:54:06.920 UTC [chaincodeCmd] chaincodeInvokeOrQuery -> INFO 00b Chaincode invoke successful. result: status:200 
2017-11-10 03:54:06.920 UTC [main] main -> INFO 00c Exiting.....

再呼叫query命令來查一下b賬戶的餘額,如果沒有計算錯,應該是210元。

[email protected]:/opt/gopath/src/github.com/hyperledger/fabric/peer# peer chaincode query -C mychannel -n devincc -c '{"Args":["query","b"]}'
2017-11-10 03:54:24.515 UTC [msp] getMspConfig -> INFO 001 intermediate certs folder not found at [/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/[email protected].example.com/msp/intermediatecerts]. Skipping.: [stat /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/[email protected].example.com/msp/intermediatecerts: no such file or directory]
2017-11-10 03:54:24.515 UTC [msp] getMspConfig -> INFO 002 crls folder not found at [/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/[email protected].example.com/msp/intermediatecerts]. Skipping.: [stat /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/[email protected].example.com/msp/crls: no such file or directory]
2017-11-10 03:54:24.515 UTC [msp] getMspConfig -> INFO 003 MSP configuration file not found at [/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/[email protected].example.com/msp/config.yaml]: [stat /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/[email protected].example.com/msp/config.yaml: no such file or directory]
2017-11-10 03:54:24.554 UTC [msp] GetLocalMSP -> DEBU 004 Returning existing local MSP
2017-11-10 03:54:24.554 UTC [msp] GetDefaultSigningIdentity -> DEBU 005 Obtaining default signing identity
2017-11-10 03:54:24.555 UTC [msp/identity] Sign -> DEBU 006 Sign: plaintext: 0AB4070A6A08031A0C08F0C594D00510...696E63631A0A0A0571756572790A0162 
2017-11-10 03:54:24.555 UTC [msp/identity] Sign -> DEBU 007 Sign: digest: 97999F9BEFEAB1D7976AA68ABF2199668568D00C6EC1CB6C7F2C83C4DF6CAA06 
Query Result: 210

測試成功。退出docker容器,然後執行下面的指令退出fabric環境。

root@pony-virtual-machine:~/gopath/src/fabric-sample/release/linux-amd64# ./network_setup.sh down

原始碼剖析

上面說了,原始碼目錄在$GOPATH/src/github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02目錄下。

下面開始分析下這個示例chaincode。

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)
}

這裡首先定義了一個SimpleChaincode類,並且實現了該類的一個方法Init

這個Init方法不是隨便定義的,根據fabric官方指引,一個chaincode需要實現如下的介面,

type Chaincode interface {
    // Init is called during Instantiate transaction after the chaincode container
    // has been established for the first time, allowing the chaincode to
    // initialize its internal data
    Init(stub ChaincodeStubInterface) pb.Response

    // Invoke is called to update or query the ledger in a proposal transaction.
    // Updated state variables are not committed to the ledger until the
    // transaction is committed.
    Invoke(stub ChaincodeStubInterface) pb.Response
}

當我們執行peer chaincode instantiate命令時,系統回撥用我們實現的Init方法完成智慧合約的初始化動作。

這裡Init的實現其實比較簡單,首先我們用GetFunctionAndParameters獲取例項化傳遞過來的所有引數,並且要求引數的個數是四個。根據上面的instantiate指令我們知道傳遞的引數是

["init","a", "100", "b","200"]

可能有人會有疑問,這裡不是5個引數嗎?

其實GetFunctionAndParameters會返回兩個值,第一個值是函式名,就是這裡的”init”,第二個值就是後面的4個引數。

a和b這裡我們表示兩個賬戶(或者是兩個人也可以),數字是他們對應的賬戶餘額。然後我們用

strconv.Atoi把數字有字串轉化為整型方便後面列印處理。

PutState會把key和value先放入fabric的寫集,並不會馬上更新賬本。(更新賬本由節點完成,chaincode後續不需要參與)

再看看接口裡的另外一個方法,

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\"")
}

可以看到Invoke並沒有做具體的事情,而是根據函式名分發到子函式處理。先來看看query查詢的操作。當我們執行

peer chaincode query -C mychannel -n devincc -c '{"Args":["query","a"]}'

系統呼叫Invoke,function是”query”,引數是”a”, 進入query方法,

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)
}

關鍵的程式碼就是GetState這裡,從賬本獲取指定key的value值,返回的是[]byte型別的值,然後通過shim.Sucess返回。

invoke和delete也都比較簡單,這裡就不多說了。

參考