Skip to content

Commit

Permalink
Merge "[FAB-2928] link installation to instantiation [part-2]"
Browse files Browse the repository at this point in the history
  • Loading branch information
binhn authored and Gerrit Code Review committed Apr 14, 2017
2 parents 3e93b76 + 1616277 commit abe1f83
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 abe1f83

Please sign in to comment.