Skip to content

Commit

Permalink
Merge "[FAB-2928] link pkg with instantiation on lccc (part-1)"
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 90573e8 + 207588e commit c627cdd
Show file tree
Hide file tree
Showing 8 changed files with 437 additions and 96 deletions.
15 changes: 10 additions & 5 deletions core/chaincode/upgrade_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,17 +83,22 @@ func upgrade2(ctx context.Context, cccid *ccprovider.CCContext,
sysCCVers := util.GetSysCCVersion()
lcccid := ccprovider.NewCCContext(cccid.ChainID, cis.ChaincodeSpec.ChaincodeId.Name, sysCCVers, uuid, true, nil, nil)

var versionBytes []byte
var cdbytes []byte
//write to lccc
if versionBytes, _, err = ExecuteWithErrorFilter(ctx, lcccid, cis); err != nil {
if cdbytes, _, err = ExecuteWithErrorFilter(ctx, lcccid, cis); err != nil {
return nil, fmt.Errorf("Error executing LCCC for upgrade: %s", err)
}

if versionBytes == nil {
return nil, fmt.Errorf("Expected version back from LCCC but got nil")
if cdbytes == nil {
return nil, fmt.Errorf("Expected ChaincodeData back from LCCC but got nil")
}

newVersion := string(versionBytes)
cd := &ccprovider.ChaincodeData{}
if err = proto.Unmarshal(cdbytes, cd); err != nil {
return nil, fmt.Errorf("getting ChaincodeData failed")
}

newVersion := string(cd.Version)
if newVersion == cccid.Version {
return nil, fmt.Errorf("Expected new version from LCCC but got same %s(%s)", newVersion, cccid.Version)
}
Expand Down
25 changes: 20 additions & 5 deletions core/common/ccprovider/ccprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -250,14 +250,29 @@ func (cccid *CCContext) GetCanonicalName() string {
return cccid.canonicalName
}

//-------- ChaincodeData is stored on the LCCC -------

//ChaincodeData defines the datastructure for chaincodes to be serialized by proto
//Type provides an additional check by directing to use a specific package after instantiation
//Data is Type specifc (see CDSPackage and SignedCDSPackage)
type ChaincodeData struct {
Name string `protobuf:"bytes,1,opt,name=name"`
//Name of the chaincode
Name string `protobuf:"bytes,1,opt,name=name"`

//Version of the chaincode
Version string `protobuf:"bytes,2,opt,name=version"`
DepSpec []byte `protobuf:"bytes,3,opt,name=depSpec,proto3"`
Escc string `protobuf:"bytes,4,opt,name=escc"`
Vscc string `protobuf:"bytes,5,opt,name=vscc"`
Policy []byte `protobuf:"bytes,6,opt,name=policy"`

//Escc for the chaincode instance
Escc string `protobuf:"bytes,3,opt,name=escc"`

//Vscc for the chaincode instance
Vscc string `protobuf:"bytes,4,opt,name=vscc"`

//Policy endorsement policy for the chaincode instance
Policy []byte `protobuf:"bytes,5,opt,name=policy,proto3"`

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

//implement functions needed from proto.Message for proto's mar/unmarshal functions
Expand Down
119 changes: 117 additions & 2 deletions core/common/ccprovider/cdspackage.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,49 @@ import (

"github.com/golang/protobuf/proto"

"bytes"

"github.com/hyperledger/fabric/bccsp"
"github.com/hyperledger/fabric/bccsp/factory"
pb "github.com/hyperledger/fabric/protos/peer"
)

//----- CDSData ------

//CDSData is data stored in the LCCC on instantiation of a CC
//for CDSPackage. This needs to be serialized for ChaincodeData
//hence the protobuf format
type CDSData struct {
//CodeHash hash of CodePackage from ChaincodeDeploymentSpec
CodeHash []byte `protobuf:"bytes,1,opt,name=codehash,proto3"`

//MetaDataHash hash of Name and Version from ChaincodeDeploymentSpec
MetaDataHash []byte `protobuf:"bytes,2,opt,name=metadatahash,proto3"`
}

//----implement functions needed from proto.Message for proto's mar/unmarshal functions

//Reset resets
func (data *CDSData) Reset() { *data = CDSData{} }

//String convers to string
func (data *CDSData) String() string { return proto.CompactTextString(data) }

//ProtoMessage just exists to make proto happy
func (*CDSData) ProtoMessage() {}

//Equals data equals other
func (data *CDSData) Equals(other *CDSData) bool {
return other != nil && bytes.Equal(data.CodeHash, other.CodeHash) && bytes.Equal(data.MetaDataHash, other.MetaDataHash)
}

//--------- CDSPackage ------------

//CDSPackage encapsulates ChaincodeDeploymentSpec.
type CDSPackage struct {
buf []byte
depSpec *pb.ChaincodeDeploymentSpec
data *CDSData
}

// GetDepSpec gets the ChaincodeDeploymentSpec from the package
Expand All @@ -42,16 +78,75 @@ func (ccpack *CDSPackage) GetPackageObject() proto.Message {
return ccpack.depSpec
}

func (ccpack *CDSPackage) getCDSData(cds *pb.ChaincodeDeploymentSpec) ([]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.
if cds == nil {
panic("nil cds")
}

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

if err = factory.InitFactories(nil); err != nil {
return 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
}

cdsdata := &CDSData{}

//code hash
cdsdata.CodeHash = hash.Sum(cds.CodePackage)

hash.Reset()

//metadata hash
hash.Write([]byte(cds.ChaincodeSpec.ChaincodeId.Name))
hash.Write([]byte(cds.ChaincodeSpec.ChaincodeId.Version))

cdsdata.MetaDataHash = hash.Sum(nil)

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

return b, 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) {
if ccpack.depSpec == nil {
return nil, fmt.Errorf("uninitialized package")
}

if ccpack.data == nil {
return nil, 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)
}
//for now just return chaincode. When we introduce Hash we will do more checks

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

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

return ccpack.depSpec, nil
}

Expand All @@ -60,22 +155,32 @@ func (ccpack *CDSPackage) InitFromBuffer(buf []byte) (*ChaincodeData, error) {
//incase ccpack is reused
ccpack.buf = nil
ccpack.depSpec = nil
ccpack.data = nil

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)
if err != nil {
return nil, err
}

ccpack.buf = buf
ccpack.depSpec = depSpec
return &ChaincodeData{Name: depSpec.ChaincodeSpec.ChaincodeId.Name, Version: depSpec.ChaincodeSpec.ChaincodeId.Version}, nil
ccpack.data = data

return &ChaincodeData{Name: depSpec.ChaincodeSpec.ChaincodeId.Name, Version: depSpec.ChaincodeSpec.ChaincodeId.Version, Data: databytes}, 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

buf, err := GetChaincodePackage(ccname, ccversion)
if err != nil {
Expand All @@ -88,8 +193,14 @@ func (ccpack *CDSPackage) InitFromFS(ccname string, ccversion string) ([]byte, *
return nil, nil, fmt.Errorf("failed to unmarshal fs deployment spec for %s, %s", ccname, ccversion)
}

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

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

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

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

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

Expand Down
71 changes: 55 additions & 16 deletions core/common/ccprovider/cdspackage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package ccprovider

import (
"fmt"
"io/ioutil"
"os"
"testing"
Expand All @@ -34,27 +35,37 @@ func setupccdir() string {
return tempDir
}

func processCDS(cds *pb.ChaincodeDeploymentSpec, tofs bool) (*CDSPackage, []byte, *ChaincodeData, error) {
b := utils.MarshalOrPanic(cds)

ccpack := &CDSPackage{}
cd, err := ccpack.InitFromBuffer(b)
if err != nil {
return nil, nil, nil, fmt.Errorf("error owner creating package %s", err)
}

if tofs {
if err = ccpack.PutChaincodeToFS(); err != nil {
return nil, nil, nil, fmt.Errorf("error putting package on the FS %s", err)
}
}

return ccpack, b, cd, nil
}

func TestPutCDSCC(t *testing.T) {
ccdir := setupccdir()
defer os.RemoveAll(ccdir)

cds := &pb.ChaincodeDeploymentSpec{ChaincodeSpec: &pb.ChaincodeSpec{Type: 1, ChaincodeId: &pb.ChaincodeID{Name: "testcc", Version: "0"}, Input: &pb.ChaincodeInput{Args: [][]byte{[]byte("")}}}, CodePackage: []byte("code")}

b := utils.MarshalOrPanic(cds)

ccpack := &CDSPackage{}
_, err := ccpack.InitFromBuffer(b)
ccpack, _, cd, err := processCDS(cds, true)
if err != nil {
t.Fatalf("error owner creating package %s", err)
t.Fatalf("error putting CDS to FS %s", err)
return
}

if err = ccpack.PutChaincodeToFS(); err != nil {
t.Fatalf("error putting package on the FS %s", err)
return
}

if _, err = ccpack.ValidateCC(&ChaincodeData{Name: "testcc", Version: "0"}); err != nil {
if _, err = ccpack.ValidateCC(cd); err != nil {
t.Fatalf("error validating package %s", err)
return
}
Expand All @@ -66,12 +77,9 @@ func TestPutCDSErrorPaths(t *testing.T) {

cds := &pb.ChaincodeDeploymentSpec{ChaincodeSpec: &pb.ChaincodeSpec{Type: 1, ChaincodeId: &pb.ChaincodeID{Name: "testcc", Version: "0"}, Input: &pb.ChaincodeInput{Args: [][]byte{[]byte("")}}}, CodePackage: []byte("code")}

b := utils.MarshalOrPanic(cds)

ccpack := &CDSPackage{}
_, err := ccpack.InitFromBuffer(b)
ccpack, b, _, err := processCDS(cds, true)
if err != nil {
t.Fatalf("error owner creating package %s", err)
t.Fatalf("error putting CDS to FS %s", err)
return
}

Expand Down Expand Up @@ -135,3 +143,34 @@ func TestCDSGetCCPackage(t *testing.T) {
return
}
}

//switch the chaincodes on the FS and validate
func TestCDSSwitchChaincodes(t *testing.T) {
ccdir := setupccdir()
defer os.RemoveAll(ccdir)

//someone modified the code on the FS with "badcode"
cds := &pb.ChaincodeDeploymentSpec{ChaincodeSpec: &pb.ChaincodeSpec{Type: 1, ChaincodeId: &pb.ChaincodeID{Name: "testcc", Version: "0"}, Input: &pb.ChaincodeInput{Args: [][]byte{[]byte("")}}}, CodePackage: []byte("badcode")}

//write the bad code to the fs
badccpack, _, _, err := processCDS(cds, true)
if err != nil {
t.Fatalf("error putting CDS to FS %s", err)
return
}

//mimic the good code ChaincodeData from the instantiate...
cds.CodePackage = []byte("goodcode")

//...and generate the CD for it (don't overwrite the bad code)
_, _, goodcd, err := processCDS(cds, false)
if err != nil {
t.Fatalf("error putting CDS to FS %s", err)
return
}

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

0 comments on commit c627cdd

Please sign in to comment.