Skip to content

Commit 2ea7cf0

Browse files
committed
Add query to get the installed chaincodes on a peer
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 <[email protected]>
1 parent ab7e1da commit 2ea7cf0

File tree

5 files changed

+157
-6
lines changed

5 files changed

+157
-6
lines changed

core/common/ccprovider/ccprovider.go

+55
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"fmt"
2222
"io/ioutil"
2323
"os"
24+
"strings"
2425

2526
"github.com/golang/protobuf/proto"
2627

@@ -107,6 +108,60 @@ func PutChaincodeIntoFS(depSpec *pb.ChaincodeDeploymentSpec) error {
107108
return nil
108109
}
109110

111+
// GetInstalledChaincodes returns a map whose key is the chaincode id and
112+
// value is the ChaincodeDeploymentSpec struct for that chaincodes that have
113+
// been installed (but not necessarily instantiated) on the peer by searching
114+
// the chaincode install path
115+
func GetInstalledChaincodes() (*pb.ChaincodeQueryResponse, error) {
116+
files, err := ioutil.ReadDir(chaincodeInstallPath)
117+
if err != nil {
118+
return nil, err
119+
}
120+
121+
// array to store info for all chaincode entries from LCCC
122+
var ccInfoArray []*pb.ChaincodeInfo
123+
124+
for _, file := range files {
125+
fileNameArray := strings.Split(file.Name(), ".")
126+
127+
// check that length is 2 as expected, otherwise skip to next cc file
128+
if len(fileNameArray) == 2 {
129+
ccname := fileNameArray[0]
130+
ccversion := fileNameArray[1]
131+
_, cdsfs, err := GetChaincodeFromFS(ccname, ccversion)
132+
if err != nil {
133+
// either chaincode on filesystem has been tampered with or
134+
// a non-chaincode file has been found in the chaincodes directory
135+
ccproviderLogger.Errorf("Unreadable chaincode file found on filesystem: %s", file.Name())
136+
continue
137+
}
138+
139+
name := cdsfs.GetChaincodeSpec().GetChaincodeId().Name
140+
version := cdsfs.GetChaincodeSpec().GetChaincodeId().Version
141+
if name != ccname || version != ccversion {
142+
// chaincode name/version in the chaincode file name has been modified
143+
// by an external entity
144+
ccproviderLogger.Errorf("Chaincode file's name/version has been modified on the filesystem: %s", file.Name())
145+
continue
146+
}
147+
148+
path := cdsfs.GetChaincodeSpec().ChaincodeId.Path
149+
// since this is just an installed chaincode these should be blank
150+
input, escc, vscc := "", "", ""
151+
152+
ccInfo := &pb.ChaincodeInfo{Name: name, Version: version, Path: path, Input: input, Escc: escc, Vscc: vscc}
153+
154+
// add this specific chaincode's metadata to the array of all chaincodes
155+
ccInfoArray = append(ccInfoArray, ccInfo)
156+
}
157+
}
158+
// add array with info about all instantiated chaincodes to the query
159+
// response proto
160+
cqr := &pb.ChaincodeQueryResponse{Chaincodes: ccInfoArray}
161+
162+
return cqr, nil
163+
}
164+
110165
//CCContext pass this around instead of string of args
111166
type CCContext struct {
112167
//ChainID chain id

core/scc/lccc/lccc.go

+26-1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ const (
6767
//GETCHAINCODES gets the instantiated chaincodes on a channel
6868
GETCHAINCODES = "getchaincodes"
6969

70+
//GETINSTALLEDCHAINCODES gets the installed chaincodes on a peer
71+
GETINSTALLEDCHAINCODES = "getinstalledchaincodes"
72+
7073
//characters used in chaincodenamespace
7174
specialChars = "/:[]${}"
7275
)
@@ -262,7 +265,7 @@ func (lccc *LifeCycleSysCC) getChaincodes(stub shim.ChaincodeStubInterface) pb.R
262265
defer itr.Close()
263266

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

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

306+
// getInstalledChaincodes returns all chaincodes installed on the peer
307+
func (lccc *LifeCycleSysCC) getInstalledChaincodes() pb.Response {
308+
// get chaincode query response proto which contains information about all
309+
// installed chaincodes
310+
cqr, err := ccprovider.GetInstalledChaincodes()
311+
if err != nil {
312+
return shim.Error(err.Error())
313+
}
314+
315+
cqrbytes, err := proto.Marshal(cqr)
316+
if err != nil {
317+
return shim.Error(err.Error())
318+
}
319+
320+
return shim.Success(cqrbytes)
321+
}
322+
303323
//do access control
304324
func (lccc *LifeCycleSysCC) acl(stub shim.ChaincodeStubInterface, chainname string, cds *pb.ChaincodeDeploymentSpec) error {
305325
return nil
@@ -597,6 +617,11 @@ func (lccc *LifeCycleSysCC) Invoke(stub shim.ChaincodeStubInterface) pb.Response
597617
return shim.Error(InvalidArgsLenErr(len(args)).Error())
598618
}
599619
return lccc.getChaincodes(stub)
620+
case GETINSTALLEDCHAINCODES:
621+
if len(args) != 1 {
622+
return shim.Error(InvalidArgsLenErr(len(args)).Error())
623+
}
624+
return lccc.getInstalledChaincodes()
600625
}
601626

602627
return shim.Error(InvalidFunctionErr(function).Error())

core/scc/lccc/lccc_test.go

+66-3
Original file line numberDiff line numberDiff line change
@@ -144,19 +144,43 @@ func TestInstall(t *testing.T) {
144144
fmt.Println("Init failed", string(res.Message))
145145
t.FailNow()
146146
}
147-
148-
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)
147+
ccname := "example02"
148+
path := "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02"
149+
version := "0"
150+
cds, err := constructDeploymentSpec(ccname, path, version, [][]byte{[]byte("init"), []byte("a"), []byte("100"), []byte("b"), []byte("200")}, false)
149151
var b []byte
150152
if b, err = proto.Marshal(cds); err != nil || b == nil {
151153
t.FailNow()
152154
}
153155

154-
//constructDeploymentSpec puts the depspec on the FS. This should fail
156+
//constructDeploymentSpec puts the depspec on the FS. This should succeed
155157
args := [][]byte{[]byte(INSTALL), b}
156158
defer os.Remove(lccctestpath + "/example02.0")
157159
if res := stub.MockInvoke("1", args); res.Status != shim.OK {
158160
t.FailNow()
159161
}
162+
163+
args = [][]byte{[]byte(GETINSTALLEDCHAINCODES)}
164+
res := stub.MockInvoke("1", args)
165+
if res.Status != shim.OK {
166+
t.FailNow()
167+
}
168+
169+
cqr := &pb.ChaincodeQueryResponse{}
170+
err = proto.Unmarshal(res.Payload, cqr)
171+
if err != nil {
172+
t.FailNow()
173+
}
174+
175+
// installed one chaincode so query should return an array with one chaincode
176+
if len(cqr.GetChaincodes()) != 1 {
177+
t.FailNow()
178+
}
179+
180+
// check that the ChaincodeInfo values match the input values
181+
if cqr.GetChaincodes()[0].Name != ccname || cqr.GetChaincodes()[0].Version != version || cqr.GetChaincodes()[0].Path != path {
182+
t.FailNow()
183+
}
160184
}
161185

162186
//TestReinstall tests the install function
@@ -555,6 +579,45 @@ func TestGetAPIsWithoutInstall(t *testing.T) {
555579
if res := stub.MockInvoke("1", args); res.Status == shim.OK {
556580
t.FailNow()
557581
}
582+
583+
// get instantiated chaincodes
584+
args = [][]byte{[]byte(GETCHAINCODES)}
585+
res := stub.MockInvoke("1", args)
586+
if res.Status != shim.OK {
587+
t.FailNow()
588+
}
589+
590+
cqr := &pb.ChaincodeQueryResponse{}
591+
err = proto.Unmarshal(res.Payload, cqr)
592+
if err != nil {
593+
t.FailNow()
594+
}
595+
596+
// one chaincode instantiated so query should return an array with one
597+
// chaincode
598+
if len(cqr.GetChaincodes()) != 1 {
599+
t.FailNow()
600+
}
601+
602+
// get installed chaincodes
603+
args = [][]byte{[]byte(GETINSTALLEDCHAINCODES)}
604+
res = stub.MockInvoke("1", args)
605+
if res.Status != shim.OK {
606+
t.FailNow()
607+
}
608+
609+
cqr = &pb.ChaincodeQueryResponse{}
610+
err = proto.Unmarshal(res.Payload, cqr)
611+
if err != nil {
612+
t.FailNow()
613+
}
614+
615+
// no chaincodes installed to FS so query should return an array with zero
616+
// chaincodes
617+
if len(cqr.GetChaincodes()) != 0 {
618+
t.FailNow()
619+
}
620+
558621
}
559622

560623
func TestMain(m *testing.M) {

protos/peer/query.pb.go

+6-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

protos/peer/query.proto

+4
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ message ChaincodeInfo {
3838
// the chaincode function upon instantiation and its arguments. This will be
3939
// blank if the query is returning information about installed chaincodes.
4040
string input = 4;
41+
// the name of the ESCC for this chaincode. This will be
42+
// blank if the query is returning information about installed chaincodes.
4143
string escc = 5;
44+
// the name of the VSCC for this chaincode. This will be
45+
// blank if the query is returning information about installed chaincodes.
4246
string vscc = 6;
4347
}

0 commit comments

Comments
 (0)