Skip to content

Commit

Permalink
[FAB-2928] link installation to instantiation [part-2]
Browse files Browse the repository at this point in the history
This adds package specific signature to LCCC on instantiations.
It also prevents launching of chaincodes if the installed
chaincode does not match the signature of the instanitated chaincode.
In particular, it ensures future installs that don't comply with the
corresponding instantiation will be useless (cannot invoke them)..

Also refactored getChaincode function per Binh's suggestion
in Part-1.

Change-Id: I107c91a80002a336e42aff95d4aed7efe4141ead
Signed-off-by: Srinivasan Muralidharan <muralisr@us.ibm.com>
  • Loading branch information
Srinivasan Muralidharan committed Apr 14, 2017
1 parent 207588e commit 1616277
Show file tree
Hide file tree
Showing 9 changed files with 421 additions and 140 deletions.
22 changes: 13 additions & 9 deletions core/chaincode/chaincode_support.go
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,7 @@ func (chaincodeSupport *ChaincodeSupport) Launch(context context.Context, cccid
var depPayload []byte

//hopefully we are restarting from existing image and the deployed transaction exists
//this will also validate the ID from the LCCC
depPayload, err = GetCDSFromLCCC(context, cccid.TxID, cccid.SignedProposal, cccid.Proposal, cccid.ChainID, cID.Name)
if err != nil {
return cID, cMsg, fmt.Errorf("Could not get deployment transaction from LCCC for %s - %s", canName, err)
Expand All @@ -558,21 +559,24 @@ func (chaincodeSupport *ChaincodeSupport) Launch(context context.Context, cccid

//launch container if it is a System container or not in dev mode
if (!chaincodeSupport.userRunsCC || cds.ExecEnv == pb.ChaincodeDeploymentSpec_SYSTEM) && (chrte == nil || chrte.handler == nil) {
//whether we deploying, upgrading or launching a chaincode we now have a
//deployment package. If lauching, we got it from LCCC and has gone through
//ccprovider.GetChaincodeFromFS
//NOTE-We need to streamline code a bit so the data from LCCC gets passed to this thus
//avoiding the need to go to the FS. In particular, we should use cdsfs completely. It is
//just a vestige of old protocol that we continue to use ChaincodeDeploymentSpec for
//anything other than Install. In particular, instantiate, invoke, upgrade should be using
//just some form of ChaincodeInvocationSpec.
//
//But for now, if we are invoking we have gone through the LCCC path above. If instantiating
//or upgrading currently we send a CDS with nil CodePackage. In this case the codepath
//in the endorser has gone through LCCC validation. Just get the code from the FS.
if cds.CodePackage == nil {
//no code bytes for these situations
if !(chaincodeSupport.userRunsCC || cds.ExecEnv == pb.ChaincodeDeploymentSpec_SYSTEM) {
_, cdsfs, err := ccprovider.GetChaincodeFromFS(cID.Name, cID.Version)
ccpack, err := ccprovider.GetChaincodeFromFS(cID.Name, cID.Version)
if err != nil {
return cID, cMsg, err
}
//we should use cdsfs completely. It is just a vestige of old protocol that we
//continue to use ChaincodeDeploymentSpec for anything other than Install. In
//particular, instantiate, invoke, upgrade should be using just some form of
//ChaincodeInvocationSpec.
cds = cdsfs

cds = ccpack.GetDepSpec()
chaincodeLogger.Debugf("launchAndWaitForRegister fetched %d from file system", len(cds.CodePackage), err)
}
}
Expand Down
3 changes: 3 additions & 0 deletions core/chaincode/upgrade_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ func upgrade2(ctx context.Context, cccid *ccprovider.CCContext,
}
}()

//ignore existence errors
ccprovider.PutChaincodeIntoFS(chaincodeDeploymentSpec)

sysCCVers := util.GetSysCCVersion()
lcccid := ccprovider.NewCCContext(cccid.ChainID, cis.ChaincodeSpec.ChaincodeId.Name, sysCCVers, uuid, true, nil, nil)

Expand Down
35 changes: 26 additions & 9 deletions core/common/ccprovider/ccprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,19 +47,28 @@ type CCPackage interface {
// InitFromFS gets the chaincode from the filesystem (includes the raw bytes too)
InitFromFS(ccname string, ccversion string) ([]byte, *pb.ChaincodeDeploymentSpec, error)

// PutChaincodeToFS writes the chaincode to the filesystem
PutChaincodeToFS() error

// GetDepSpec gets the ChaincodeDeploymentSpec from the package
GetDepSpec() *pb.ChaincodeDeploymentSpec

// PutChaincodeToFS writes the chaincode to the filesystem
PutChaincodeToFS() error
// GetDepSpecBytes gets the serialized ChaincodeDeploymentSpec from the package
GetDepSpecBytes() []byte

// ValidateCC validates and returns the chaincode deployment spec corresponding to
// ChaincodeData. The validation is based on the metadata from ChaincodeData
// One use of this method is to validate the chaincode before launching
ValidateCC(ccdata *ChaincodeData) (*pb.ChaincodeDeploymentSpec, error)
ValidateCC(ccdata *ChaincodeData) error

// GetPackageObject gets the object as a proto.Message
GetPackageObject() proto.Message

// GetChaincodeData gets the ChaincodeData
GetChaincodeData() *ChaincodeData

// GetId gets the fingerprint of the chaincode based on package computation
GetId() []byte
}

//SetChaincodesPath sets the chaincode path for this peer
Expand Down Expand Up @@ -91,19 +100,20 @@ func GetChaincodePackage(ccname string, ccversion string) ([]byte, error) {
}

// GetChaincodeFromFS this is a wrapper for hiding package implementation.
func GetChaincodeFromFS(ccname string, ccversion string) ([]byte, *pb.ChaincodeDeploymentSpec, error) {
func GetChaincodeFromFS(ccname string, ccversion string) (CCPackage, error) {
//try raw CDS
cccdspack := &CDSPackage{}
b, depSpec, err := cccdspack.InitFromFS(ccname, ccversion)
_, _, err := cccdspack.InitFromFS(ccname, ccversion)
if err != nil {
//try signed CDS
ccscdspack := &SignedCDSPackage{}
b, depSpec, err = ccscdspack.InitFromFS(ccname, ccversion)
_, _, err = ccscdspack.InitFromFS(ccname, ccversion)
if err != nil {
return nil, nil, err
return nil, err
}
return ccscdspack, nil
}
return b, depSpec, nil
return cccdspack, nil
}

// PutChaincodeIntoFS is a wrapper for putting raw ChaincodeDeploymentSpec
Expand Down Expand Up @@ -158,14 +168,16 @@ func GetInstalledChaincodes() (*pb.ChaincodeQueryResponse, error) {
if len(fileNameArray) == 2 {
ccname := fileNameArray[0]
ccversion := fileNameArray[1]
_, cdsfs, err := GetChaincodeFromFS(ccname, ccversion)
ccpack, 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
}

cdsfs := ccpack.GetDepSpec()

name := cdsfs.GetChaincodeSpec().GetChaincodeId().Name
version := cdsfs.GetChaincodeSpec().GetChaincodeId().Version
if name != ccname || version != ccversion {
Expand Down Expand Up @@ -273,6 +285,11 @@ type ChaincodeData struct {

//Data data specific to the package
Data []byte `protobuf:"bytes,6,opt,name=data,proto3"`

//Id of the chaincode that's the unique fingerprint for the CC
//This is not currently used anywhere but serves as a good
//eyecatcher
Id []byte `protobuf:"bytes,7,opt,name=id,proto3"`
}

//implement functions needed from proto.Message for proto's mar/unmarshal functions
Expand Down
113 changes: 79 additions & 34 deletions core/common/ccprovider/cdspackage.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,19 +66,61 @@ type CDSPackage struct {
buf []byte
depSpec *pb.ChaincodeDeploymentSpec
data *CDSData
datab []byte
id []byte
}

// resets data
func (ccpack *CDSPackage) reset() {
*ccpack = CDSPackage{}
}

// GetId gets the fingerprint of the chaincode based on package computation
func (ccpack *CDSPackage) GetId() []byte {
//this has to be after creating a package and initializing it
//If those steps fail, GetId() should never be called
if ccpack.id == nil {
panic("GetId called on uninitialized package")
}
return ccpack.id
}

// GetDepSpec gets the ChaincodeDeploymentSpec from the package
func (ccpack *CDSPackage) GetDepSpec() *pb.ChaincodeDeploymentSpec {
//this has to be after creating a package and initializing it
//If those steps fail, GetDepSpec() should never be called
if ccpack.depSpec == nil {
panic("GetDepSpec called on uninitialized package")
}
return ccpack.depSpec
}

// GetDepSpecBytes gets the serialized ChaincodeDeploymentSpec from the package
func (ccpack *CDSPackage) GetDepSpecBytes() []byte {
//this has to be after creating a package and initializing it
//If those steps fail, GetDepSpecBytes() should never be called
if ccpack.buf == nil {
panic("GetDepSpecBytes called on uninitialized package")
}
return ccpack.buf
}

// GetPackageObject gets the ChaincodeDeploymentSpec as proto.Message
func (ccpack *CDSPackage) GetPackageObject() proto.Message {
return ccpack.depSpec
}

func (ccpack *CDSPackage) getCDSData(cds *pb.ChaincodeDeploymentSpec) ([]byte, *CDSData, error) {
// GetChaincodeData gets the ChaincodeData
func (ccpack *CDSPackage) GetChaincodeData() *ChaincodeData {
//this has to be after creating a package and initializing it
//If those steps fail, GetChaincodeData() should never be called
if ccpack.depSpec == nil || ccpack.datab == nil || ccpack.id == nil {
panic("GetChaincodeData called on uninitialized package")
}
return &ChaincodeData{Name: ccpack.depSpec.ChaincodeSpec.ChaincodeId.Name, Version: ccpack.depSpec.ChaincodeSpec.ChaincodeId.Version, Data: ccpack.datab, Id: ccpack.id}
}

func (ccpack *CDSPackage) getCDSData(cds *pb.ChaincodeDeploymentSpec) ([]byte, []byte, *CDSData, error) {
// check for nil argument. It is an assertion that getCDSData
// is never called on a package that did not go through/succeed
// package initialization.
Expand All @@ -88,17 +130,17 @@ func (ccpack *CDSPackage) getCDSData(cds *pb.ChaincodeDeploymentSpec) ([]byte, *

b, err := proto.Marshal(cds)
if err != nil {
return nil, nil, err
return nil, nil, nil, err
}

if err = factory.InitFactories(nil); err != nil {
return nil, nil, fmt.Errorf("Internal error, BCCSP could not be initialized : %s", err)
return nil, nil, nil, fmt.Errorf("Internal error, BCCSP could not be initialized : %s", err)
}

//compute hashes now
hash, err := factory.GetDefault().GetHash(&bccsp.SHAOpts{})
if err != nil {
return nil, nil, err
return nil, nil, nil, err
}

cdsdata := &CDSData{}
Expand All @@ -116,93 +158,88 @@ func (ccpack *CDSPackage) getCDSData(cds *pb.ChaincodeDeploymentSpec) ([]byte, *

b, err = proto.Marshal(cdsdata)
if err != nil {
return nil, nil, err
return nil, nil, nil, err
}

return b, cdsdata, nil
hash.Reset()

//compute the id
hash.Write(cdsdata.CodeHash)
hash.Write(cdsdata.MetaDataHash)

id := hash.Sum(nil)

return b, id, cdsdata, nil
}

// ValidateCC returns error if the chaincode is not found or if its not a
// ChaincodeDeploymentSpec
func (ccpack *CDSPackage) ValidateCC(ccdata *ChaincodeData) (*pb.ChaincodeDeploymentSpec, error) {
func (ccpack *CDSPackage) ValidateCC(ccdata *ChaincodeData) error {
if ccpack.depSpec == nil {
return nil, fmt.Errorf("uninitialized package")
return fmt.Errorf("uninitialized package")
}

if ccpack.data == nil {
return nil, fmt.Errorf("nil data")
return fmt.Errorf("nil data")
}

if ccdata.Name != ccpack.depSpec.ChaincodeSpec.ChaincodeId.Name || ccdata.Version != ccpack.depSpec.ChaincodeSpec.ChaincodeId.Version {
return nil, fmt.Errorf("invalid chaincode data %v (%v)", ccdata, ccpack.depSpec.ChaincodeSpec.ChaincodeId)
return fmt.Errorf("invalid chaincode data %v (%v)", ccdata, ccpack.depSpec.ChaincodeSpec.ChaincodeId)
}

otherdata := &CDSData{}
err := proto.Unmarshal(ccdata.Data, otherdata)
if err != nil {
return nil, err
return err
}

if !ccpack.data.Equals(otherdata) {
return nil, fmt.Errorf("data mismatch")
return fmt.Errorf("data mismatch")
}

return ccpack.depSpec, nil
return nil
}

//InitFromBuffer sets the buffer if valid and returns ChaincodeData
func (ccpack *CDSPackage) InitFromBuffer(buf []byte) (*ChaincodeData, error) {
//incase ccpack is reused
ccpack.buf = nil
ccpack.depSpec = nil
ccpack.data = nil
ccpack.reset()

depSpec := &pb.ChaincodeDeploymentSpec{}
err := proto.Unmarshal(buf, depSpec)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal deployment spec from bytes")
}

databytes, data, err := ccpack.getCDSData(depSpec)
databytes, id, data, err := ccpack.getCDSData(depSpec)
if err != nil {
return nil, err
}

ccpack.buf = buf
ccpack.depSpec = depSpec
ccpack.data = data
ccpack.datab = databytes
ccpack.id = id

return &ChaincodeData{Name: depSpec.ChaincodeSpec.ChaincodeId.Name, Version: depSpec.ChaincodeSpec.ChaincodeId.Version, Data: databytes}, nil
return ccpack.GetChaincodeData(), nil
}

//InitFromFS returns the chaincode and its package from the file system
func (ccpack *CDSPackage) InitFromFS(ccname string, ccversion string) ([]byte, *pb.ChaincodeDeploymentSpec, error) {
//incase ccpack is reused
ccpack.buf = nil
ccpack.depSpec = nil
ccpack.data = nil
ccpack.reset()

buf, err := GetChaincodePackage(ccname, ccversion)
if err != nil {
return nil, nil, err
}

depSpec := &pb.ChaincodeDeploymentSpec{}
err = proto.Unmarshal(buf, depSpec)
if err != nil {
return nil, nil, fmt.Errorf("failed to unmarshal fs deployment spec for %s, %s", ccname, ccversion)
}

_, data, err := ccpack.getCDSData(depSpec)
if err != nil {
if _, err = ccpack.InitFromBuffer(buf); err != nil {
return nil, nil, err
}

ccpack.buf = buf
ccpack.depSpec = depSpec
ccpack.data = data

return buf, depSpec, nil
return ccpack.buf, ccpack.depSpec, nil
}

//PutChaincodeToFS - serializes chaincode to a package on the file system
Expand All @@ -211,6 +248,10 @@ func (ccpack *CDSPackage) PutChaincodeToFS() error {
return fmt.Errorf("uninitialized package")
}

if ccpack.id == nil {
return fmt.Errorf("id cannot be nil if buf is not nil")
}

if ccpack.depSpec == nil {
return fmt.Errorf("depspec cannot be nil if buf is not nil")
}
Expand All @@ -219,6 +260,10 @@ func (ccpack *CDSPackage) PutChaincodeToFS() error {
return fmt.Errorf("nil data")
}

if ccpack.datab == nil {
return fmt.Errorf("nil data bytes")
}

ccname := ccpack.depSpec.ChaincodeSpec.ChaincodeId.Name
ccversion := ccpack.depSpec.ChaincodeSpec.ChaincodeId.Version

Expand Down
6 changes: 3 additions & 3 deletions core/common/ccprovider/cdspackage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func TestPutCDSCC(t *testing.T) {
return
}

if _, err = ccpack.ValidateCC(cd); err != nil {
if err = ccpack.ValidateCC(cd); err != nil {
t.Fatalf("error validating package %s", err)
return
}
Expand All @@ -84,7 +84,7 @@ func TestPutCDSErrorPaths(t *testing.T) {
}

//validate with invalid name
if _, err = ccpack.ValidateCC(&ChaincodeData{Name: "invalname", Version: "0"}); err == nil {
if err = ccpack.ValidateCC(&ChaincodeData{Name: "invalname", Version: "0"}); err == nil {
t.Fatalf("expected error validating package")
return
}
Expand Down Expand Up @@ -169,7 +169,7 @@ func TestCDSSwitchChaincodes(t *testing.T) {
return
}

if _, err = badccpack.ValidateCC(goodcd); err == nil {
if err = badccpack.ValidateCC(goodcd); err == nil {
t.Fatalf("expected goodcd to fail against bad package but succeeded!")
return
}
Expand Down
Loading

0 comments on commit 1616277

Please sign in to comment.