Skip to content

Commit

Permalink
FAB-11523 Lifecycle SCC QueryInstalledChaincode
Browse files Browse the repository at this point in the history
This CR adds a new QueryInstalledChaincode function to the +lifecycle
system chaincode.

Makes #Done FAB-11523

Change-Id: I81dcfb0cb7f6d62d5c1893134e3f14d7e195b169
Signed-off-by: Jason Yellick <jyellick@us.ibm.com>
  • Loading branch information
Jason Yellick authored and wlahti committed Sep 26, 2018
1 parent eb42d21 commit e6a852e
Show file tree
Hide file tree
Showing 8 changed files with 408 additions and 22 deletions.
13 changes: 13 additions & 0 deletions core/chaincode/lifecycle/lifecycle.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ SPDX-License-Identifier: Apache-2.0
package lifecycle

import (
"fmt"

"github.com/hyperledger/fabric/core/chaincode/persistence"

"github.com/pkg/errors"
Expand All @@ -15,6 +17,7 @@ import (
// ChaincodeStore provides a way to persist chaincodes
type ChaincodeStore interface {
Save(name, version string, ccInstallPkg []byte) (hash []byte, err error)
RetrieveHash(name, version string) (hash []byte, err error)
}

type PackageParser interface {
Expand Down Expand Up @@ -44,3 +47,13 @@ func (l *Lifecycle) InstallChaincode(name, version string, chaincodeInstallPacka

return hash, nil
}

// QueryInstalledChaincode returns the hash of an installed chaincode of a given name and version.
func (l *Lifecycle) QueryInstalledChaincode(name, version string) ([]byte, error) {
hash, err := l.ChaincodeStore.RetrieveHash(name, version)
if err != nil {
return nil, errors.WithMessage(err, fmt.Sprintf("could not retrieve hash for chaincode '%s:%s'", name, version))
}

return hash, nil
}
27 changes: 27 additions & 0 deletions core/chaincode/lifecycle/lifecycle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,31 @@ var _ = Describe("Lifecycle", func() {
})
})
})

Describe("QueryInstalledChaincode", func() {
BeforeEach(func() {
fakeCCStore.RetrieveHashReturns([]byte("fake-hash"), nil)
})

It("passes through to the backing chaincode store", func() {
hash, err := l.QueryInstalledChaincode("name", "version")
Expect(err).NotTo(HaveOccurred())
Expect(hash).To(Equal([]byte("fake-hash")))
Expect(fakeCCStore.RetrieveHashCallCount()).To(Equal(1))
name, version := fakeCCStore.RetrieveHashArgsForCall(0)
Expect(name).To(Equal("name"))
Expect(version).To(Equal("version"))
})

Context("when the backing chaincode store fails to retrieve the hash", func() {
BeforeEach(func() {
fakeCCStore.RetrieveHashReturns(nil, fmt.Errorf("fake-error"))
})
It("wraps and returns the error", func() {
hash, err := l.QueryInstalledChaincode("name", "version")
Expect(hash).To(BeNil())
Expect(err).To(MatchError("could not retrieve hash for chaincode 'name:version': fake-error"))
})
})
})
})
68 changes: 68 additions & 0 deletions core/chaincode/lifecycle/mock/chaincode_store.go

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

68 changes: 68 additions & 0 deletions core/chaincode/lifecycle/mock/scc_functions.go

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

31 changes: 31 additions & 0 deletions core/chaincode/lifecycle/scc.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,21 @@ import (
)

const (
//InstalledChaincodeFuncName is the chaincode function name used to install a chaincode
InstallChaincodeFuncName = "InstallChaincode"

// QueryInstalledChaincodeFuncName is the chaincode function name used to query an installed chaincode
QueryInstalledChaincodeFuncName = "QueryInstalledChaincode"
)

// SCCFunctions provides a backing implementation with concrete arguments
// for each of the SCC functions
type SCCFunctions interface {
// InstallChaincode persists a chaincode definition to disk
InstallChaincode(name, version string, chaincodePackage []byte) (hash []byte, err error)

// QueryInstalledChaincode returns the hash for a given name and version of an installed chaincode
QueryInstalledChaincode(name, version string) (hash []byte, err error)
}

// SCC implements the required methods to satisfy the chaincode interface.
Expand Down Expand Up @@ -115,6 +123,29 @@ func (scc *SCC) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
return shim.Error(err.Error())
}

return shim.Success(resultBytes)
case QueryInstalledChaincodeFuncName:
input := &lb.QueryInstalledChaincodeArgs{}
err := scc.Protobuf.Unmarshal(inputBytes, input)
if err != nil {
err = errors.WithMessage(err, "failed to decode input arg to QueryInstalledChaincode")
return shim.Error(err.Error())
}

hash, err := scc.Functions.QueryInstalledChaincode(input.Name, input.Version)
if err != nil {
err = errors.WithMessage(err, "failed to invoke backing QueryInstalledChaincode")
return shim.Error(err.Error())
}

resultBytes, err := scc.Protobuf.Marshal(&lb.QueryInstalledChaincodeResult{
Hash: hash,
})
if err != nil {
err = errors.WithMessage(err, "failed to marshal result")
return shim.Error(err.Error())
}

return shim.Success(resultBytes)
default:
return shim.Error(fmt.Sprintf("unknown lifecycle function: %s", funcName))
Expand Down
75 changes: 75 additions & 0 deletions core/chaincode/lifecycle/scc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,5 +195,80 @@ var _ = Describe("SCC", func() {
})
})
})

Describe("QueryInstalledChaincode", func() {
var (
arg *lb.QueryInstalledChaincodeArgs
marshaledArg []byte
)

BeforeEach(func() {
arg = &lb.QueryInstalledChaincodeArgs{
Name: "name",
Version: "version",
}

var err error
marshaledArg, err = proto.Marshal(arg)
Expect(err).NotTo(HaveOccurred())

fakeStub.GetArgsReturns([][]byte{[]byte("QueryInstalledChaincode"), marshaledArg})

fakeProto.UnmarshalStub = proto.Unmarshal
fakeProto.MarshalStub = proto.Marshal

fakeSCCFuncs.QueryInstalledChaincodeReturns([]byte("fake-hash"), nil)
})

It("passes the arguments to and returns the results from the backing scc function implementation", func() {
res := scc.Invoke(fakeStub)
Expect(res.Status).To(Equal(int32(200)))
payload := &lb.QueryInstalledChaincodeResult{}
err := proto.Unmarshal(res.Payload, payload)
Expect(err).NotTo(HaveOccurred())
Expect(payload.Hash).To(Equal([]byte("fake-hash")))

Expect(fakeSCCFuncs.QueryInstalledChaincodeCallCount()).To(Equal(1))
name, version := fakeSCCFuncs.QueryInstalledChaincodeArgsForCall(0)
Expect(name).To(Equal("name"))
Expect(version).To(Equal("version"))
})

Context("when the underlying function implementation fails", func() {
BeforeEach(func() {
fakeSCCFuncs.QueryInstalledChaincodeReturns(nil, fmt.Errorf("underlying-error"))
})

It("wraps and returns the error", func() {
res := scc.Invoke(fakeStub)
Expect(res.Status).To(Equal(int32(500)))
Expect(res.Message).To(Equal("failed to invoke backing QueryInstalledChaincode: underlying-error"))
})
})

Context("when unmarshaling the input fails", func() {
BeforeEach(func() {
fakeProto.UnmarshalReturns(fmt.Errorf("unmarshal-error"))
})

It("wraps and returns the error", func() {
res := scc.Invoke(fakeStub)
Expect(res.Status).To(Equal(int32(500)))
Expect(res.Message).To(Equal("failed to decode input arg to QueryInstalledChaincode: unmarshal-error"))
})
})

Context("when marshaling the output fails", func() {
BeforeEach(func() {
fakeProto.MarshalReturns(nil, fmt.Errorf("marshal-error"))
})

It("wraps and returns the error", func() {
res := scc.Invoke(fakeStub)
Expect(res.Status).To(Equal(int32(500)))
Expect(res.Message).To(Equal("failed to marshal result: marshal-error"))
})
})
})
})
})
Loading

0 comments on commit e6a852e

Please sign in to comment.