Skip to content

Commit

Permalink
Add query to get the installed chaincodes on a peer
Browse files Browse the repository at this point in the history
This change adds a query which returns the details of all
chaincodes installed on a peer. The return value is a
ChaincodeQueryResponse proto which contains an array of
ChaincodeInfo protos, which each contain the chaincode name,
version, and path (the input arguments, ESCC name, and VSCC
name will all be blank since those fields are  info only
related to instantiating a chaincode).

https://jira.hyperledger.org/browse/FAB-2235

Change-Id: I5154678729211f62c1f686c99dafa598613e49ea
Signed-off-by: Will Lahti <wtlahti@us.ibm.com>
  • Loading branch information
wlahti committed Feb 24, 2017
1 parent ab7e1da commit 2ea7cf0
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 6 deletions.
55 changes: 55 additions & 0 deletions core/common/ccprovider/ccprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"fmt"
"io/ioutil"
"os"
"strings"

"github.com/golang/protobuf/proto"

Expand Down Expand Up @@ -107,6 +108,60 @@ func PutChaincodeIntoFS(depSpec *pb.ChaincodeDeploymentSpec) error {
return nil
}

// GetInstalledChaincodes returns a map whose key is the chaincode id and
// value is the ChaincodeDeploymentSpec struct for that chaincodes that have
// been installed (but not necessarily instantiated) on the peer by searching
// the chaincode install path
func GetInstalledChaincodes() (*pb.ChaincodeQueryResponse, error) {
files, err := ioutil.ReadDir(chaincodeInstallPath)
if err != nil {
return nil, err
}

// array to store info for all chaincode entries from LCCC
var ccInfoArray []*pb.ChaincodeInfo

for _, file := range files {
fileNameArray := strings.Split(file.Name(), ".")

// check that length is 2 as expected, otherwise skip to next cc file
if len(fileNameArray) == 2 {
ccname := fileNameArray[0]
ccversion := fileNameArray[1]
_, cdsfs, err := GetChaincodeFromFS(ccname, ccversion)
if err != nil {
// either chaincode on filesystem has been tampered with or
// a non-chaincode file has been found in the chaincodes directory
ccproviderLogger.Errorf("Unreadable chaincode file found on filesystem: %s", file.Name())
continue
}

name := cdsfs.GetChaincodeSpec().GetChaincodeId().Name
version := cdsfs.GetChaincodeSpec().GetChaincodeId().Version
if name != ccname || version != ccversion {
// chaincode name/version in the chaincode file name has been modified
// by an external entity
ccproviderLogger.Errorf("Chaincode file's name/version has been modified on the filesystem: %s", file.Name())
continue
}

path := cdsfs.GetChaincodeSpec().ChaincodeId.Path
// since this is just an installed chaincode these should be blank
input, escc, vscc := "", "", ""

ccInfo := &pb.ChaincodeInfo{Name: name, Version: version, Path: path, Input: input, Escc: escc, Vscc: vscc}

// add this specific chaincode's metadata to the array of all chaincodes
ccInfoArray = append(ccInfoArray, ccInfo)
}
}
// add array with info about all instantiated chaincodes to the query
// response proto
cqr := &pb.ChaincodeQueryResponse{Chaincodes: ccInfoArray}

return cqr, nil
}

//CCContext pass this around instead of string of args
type CCContext struct {
//ChainID chain id
Expand Down
27 changes: 26 additions & 1 deletion core/scc/lccc/lccc.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ const (
//GETCHAINCODES gets the instantiated chaincodes on a channel
GETCHAINCODES = "getchaincodes"

//GETINSTALLEDCHAINCODES gets the installed chaincodes on a peer
GETINSTALLEDCHAINCODES = "getinstalledchaincodes"

//characters used in chaincodenamespace
specialChars = "/:[]${}"
)
Expand Down Expand Up @@ -262,7 +265,7 @@ func (lccc *LifeCycleSysCC) getChaincodes(stub shim.ChaincodeStubInterface) pb.R
defer itr.Close()

// array to store metadata for all chaincode entries from LCCC
ccInfoArray := make([]*pb.ChaincodeInfo, 0)
var ccInfoArray []*pb.ChaincodeInfo

for itr.HasNext() {
_, value, err := itr.Next()
Expand Down Expand Up @@ -300,6 +303,23 @@ func (lccc *LifeCycleSysCC) getChaincodes(stub shim.ChaincodeStubInterface) pb.R
return shim.Success(cqrbytes)
}

// getInstalledChaincodes returns all chaincodes installed on the peer
func (lccc *LifeCycleSysCC) getInstalledChaincodes() pb.Response {
// get chaincode query response proto which contains information about all
// installed chaincodes
cqr, err := ccprovider.GetInstalledChaincodes()
if err != nil {
return shim.Error(err.Error())
}

cqrbytes, err := proto.Marshal(cqr)
if err != nil {
return shim.Error(err.Error())
}

return shim.Success(cqrbytes)
}

//do access control
func (lccc *LifeCycleSysCC) acl(stub shim.ChaincodeStubInterface, chainname string, cds *pb.ChaincodeDeploymentSpec) error {
return nil
Expand Down Expand Up @@ -597,6 +617,11 @@ func (lccc *LifeCycleSysCC) Invoke(stub shim.ChaincodeStubInterface) pb.Response
return shim.Error(InvalidArgsLenErr(len(args)).Error())
}
return lccc.getChaincodes(stub)
case GETINSTALLEDCHAINCODES:
if len(args) != 1 {
return shim.Error(InvalidArgsLenErr(len(args)).Error())
}
return lccc.getInstalledChaincodes()
}

return shim.Error(InvalidFunctionErr(function).Error())
Expand Down
69 changes: 66 additions & 3 deletions core/scc/lccc/lccc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,19 +144,43 @@ func TestInstall(t *testing.T) {
fmt.Println("Init failed", string(res.Message))
t.FailNow()
}

cds, err := constructDeploymentSpec("example02", "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02", "0", [][]byte{[]byte("init"), []byte("a"), []byte("100"), []byte("b"), []byte("200")}, false)
ccname := "example02"
path := "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02"
version := "0"
cds, err := constructDeploymentSpec(ccname, path, version, [][]byte{[]byte("init"), []byte("a"), []byte("100"), []byte("b"), []byte("200")}, false)
var b []byte
if b, err = proto.Marshal(cds); err != nil || b == nil {
t.FailNow()
}

//constructDeploymentSpec puts the depspec on the FS. This should fail
//constructDeploymentSpec puts the depspec on the FS. This should succeed
args := [][]byte{[]byte(INSTALL), b}
defer os.Remove(lccctestpath + "/example02.0")
if res := stub.MockInvoke("1", args); res.Status != shim.OK {
t.FailNow()
}

args = [][]byte{[]byte(GETINSTALLEDCHAINCODES)}
res := stub.MockInvoke("1", args)
if res.Status != shim.OK {
t.FailNow()
}

cqr := &pb.ChaincodeQueryResponse{}
err = proto.Unmarshal(res.Payload, cqr)
if err != nil {
t.FailNow()
}

// installed one chaincode so query should return an array with one chaincode
if len(cqr.GetChaincodes()) != 1 {
t.FailNow()
}

// check that the ChaincodeInfo values match the input values
if cqr.GetChaincodes()[0].Name != ccname || cqr.GetChaincodes()[0].Version != version || cqr.GetChaincodes()[0].Path != path {
t.FailNow()
}
}

//TestReinstall tests the install function
Expand Down Expand Up @@ -555,6 +579,45 @@ func TestGetAPIsWithoutInstall(t *testing.T) {
if res := stub.MockInvoke("1", args); res.Status == shim.OK {
t.FailNow()
}

// get instantiated chaincodes
args = [][]byte{[]byte(GETCHAINCODES)}
res := stub.MockInvoke("1", args)
if res.Status != shim.OK {
t.FailNow()
}

cqr := &pb.ChaincodeQueryResponse{}
err = proto.Unmarshal(res.Payload, cqr)
if err != nil {
t.FailNow()
}

// one chaincode instantiated so query should return an array with one
// chaincode
if len(cqr.GetChaincodes()) != 1 {
t.FailNow()
}

// get installed chaincodes
args = [][]byte{[]byte(GETINSTALLEDCHAINCODES)}
res = stub.MockInvoke("1", args)
if res.Status != shim.OK {
t.FailNow()
}

cqr = &pb.ChaincodeQueryResponse{}
err = proto.Unmarshal(res.Payload, cqr)
if err != nil {
t.FailNow()
}

// no chaincodes installed to FS so query should return an array with zero
// chaincodes
if len(cqr.GetChaincodes()) != 0 {
t.FailNow()
}

}

func TestMain(m *testing.M) {
Expand Down
8 changes: 6 additions & 2 deletions protos/peer/query.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions protos/peer/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ message ChaincodeInfo {
// the chaincode function upon instantiation and its arguments. This will be
// blank if the query is returning information about installed chaincodes.
string input = 4;
// the name of the ESCC for this chaincode. This will be
// blank if the query is returning information about installed chaincodes.
string escc = 5;
// the name of the VSCC for this chaincode. This will be
// blank if the query is returning information about installed chaincodes.
string vscc = 6;
}

0 comments on commit 2ea7cf0

Please sign in to comment.