diff --git a/core/common/ccprovider/cc_statedb_artifacts_provider.go b/core/common/ccprovider/cc_statedb_artifacts_provider.go index 69d5acd8252..f19a13efa77 100644 --- a/core/common/ccprovider/cc_statedb_artifacts_provider.go +++ b/core/common/ccprovider/cc_statedb_artifacts_provider.go @@ -21,7 +21,7 @@ const ( // ExtractStatedbArtifactsAsTarbytes extracts the statedb artifacts from the code package tar and create a statedb artifact tar. // The state db artifacts are expected to contain state db specific artifacts such as index specification in the case of couchdb. // This function is intented to be used during chaincode instantiate/upgrade so that statedb artifacts can be created. -func ExtractStatedbArtifactsAsTarbytes(ccname, ccversion string) (installed bool, statedbArtifactsTar []byte, err error) { +func ExtractStatedbArtifactsForChaincode(ccname, ccversion string) (installed bool, statedbArtifactsTar []byte, err error) { ccpackage, err := GetChaincodeFromFS(ccname, ccversion) if err != nil { // TODO for now, we assume that an error indicates that the chaincode is not installed on the peer. @@ -31,12 +31,21 @@ func ExtractStatedbArtifactsAsTarbytes(ccname, ccversion string) (installed bool return false, nil, nil } + statedbArtifactsTar, err = ExtractStatedbArtifactsFromCCPackage(ccpackage) + return true, statedbArtifactsTar, err +} + +// ExtractStatedbArtifactsFromCCPackage extracts the statedb artifacts from the code package tar and create a statedb artifact tar. +// The state db artifacts are expected to contain state db specific artifacts such as index specification in the case of couchdb. +// This function is called during chaincode instantiate/upgrade (from above), and from install, so that statedb artifacts can be created. +func ExtractStatedbArtifactsFromCCPackage(ccpackage CCPackage) (statedbArtifactsTar []byte, err error) { + cds := ccpackage.GetDepSpec() is := bytes.NewReader(cds.CodePackage) gr, err := gzip.NewReader(is) if err != nil { ccproviderLogger.Errorf("Failure opening codepackage gzip stream: %s", err) - return true, nil, err + return nil, err } tr := tar.NewReader(gr) statedbTarBuffer := bytes.NewBuffer(nil) @@ -52,7 +61,7 @@ func ExtractStatedbArtifactsAsTarbytes(ccname, ccversion string) (installed bool } if err != nil { - return true, nil, err + return nil, err } ccproviderLogger.Debugf("header.Name = %s", header.Name) if !strings.HasPrefix(header.Name, ccPackageStatedbDir) { @@ -60,17 +69,17 @@ func ExtractStatedbArtifactsAsTarbytes(ccname, ccversion string) (installed bool } if err = tw.WriteHeader(header); err != nil { ccproviderLogger.Error("Error adding header to statedb tar:", err, header.Name) - return true, nil, err + return nil, err } if _, err := io.Copy(tw, tr); err != nil { ccproviderLogger.Error("Error copying file to statedb tar:", err, header.Name) - return true, nil, err + return nil, err } ccproviderLogger.Debug("Wrote file to statedb tar:", header.Name) } if err = tw.Close(); err != nil { - return true, nil, err + return nil, err } ccproviderLogger.Debug("Created statedb artifact tar") - return true, statedbTarBuffer.Bytes(), nil + return statedbTarBuffer.Bytes(), nil } diff --git a/core/ledger/cceventmgmt/defs.go b/core/ledger/cceventmgmt/defs.go index db36156b524..408fa552c6d 100644 --- a/core/ledger/cceventmgmt/defs.go +++ b/core/ledger/cceventmgmt/defs.go @@ -50,5 +50,5 @@ func (p *chaincodeInfoProviderImpl) IsChaincodeDeployed(chainid string, chaincod // RetrieveChaincodeArtifacts implements function in the interface ChaincodeInfoProvider func (p *chaincodeInfoProviderImpl) RetrieveChaincodeArtifacts(chaincodeDefinition *ChaincodeDefinition) (installed bool, dbArtifactsTar []byte, err error) { - return ccprovider.ExtractStatedbArtifactsAsTarbytes(chaincodeDefinition.Name, chaincodeDefinition.Version) + return ccprovider.ExtractStatedbArtifactsForChaincode(chaincodeDefinition.Name, chaincodeDefinition.Version) } diff --git a/core/scc/lscc/lscc.go b/core/scc/lscc/lscc.go index 83aa78e466a..72866eb9c62 100644 --- a/core/scc/lscc/lscc.go +++ b/core/scc/lscc/lscc.go @@ -29,6 +29,7 @@ import ( "github.com/hyperledger/fabric/core/common/ccprovider" "github.com/hyperledger/fabric/core/common/privdata" "github.com/hyperledger/fabric/core/common/sysccprovider" + "github.com/hyperledger/fabric/core/ledger/cceventmgmt" "github.com/hyperledger/fabric/core/peer" "github.com/hyperledger/fabric/core/policy" "github.com/hyperledger/fabric/core/policyprovider" @@ -395,6 +396,27 @@ func (lscc *lifeCycleSysCC) executeInstall(stub shim.ChaincodeStubInterface, ccb return err } + // Get any statedb artifacts from the chaincode package, e.g. couchdb index definitions + statedbArtifactsTar, err := ccprovider.ExtractStatedbArtifactsFromCCPackage(ccpack) + if err != nil { + return err + } + + chaincodeDefinition := &cceventmgmt.ChaincodeDefinition{ + Name: ccpack.GetChaincodeData().Name, + Version: ccpack.GetChaincodeData().Version, + Hash: ccpack.GetId()} // Note - The chaincode 'id' is the hash of chaincode's (CodeHash || MetaDataHash), aka fingerprint + + // HandleChaincodeInstall will apply any statedb artifacts (e.g. couchdb indexes) to + // any channel's statedb where the chaincode is already instantiated + // Note - this step is done prior to PutChaincodeToLocalStorage() since this step is idempotent and harmless until endorsements start, + // that is, if there are errors deploying the indexes the chaincode install can safely be re-attempted later. + err = cceventmgmt.GetMgr().HandleChaincodeInstall(chaincodeDefinition, statedbArtifactsTar) + if err != nil { + return err + } + + // Finally, if everything is good above, install the chaincode to local peer file system so that endorsements can start if err = lscc.support.PutChaincodeToLocalStorage(ccpack); err != nil { return err } diff --git a/core/scc/lscc/lscc_test.go b/core/scc/lscc/lscc_test.go index b70a8534a75..97bdd2dd720 100644 --- a/core/scc/lscc/lscc_test.go +++ b/core/scc/lscc/lscc_test.go @@ -27,6 +27,7 @@ import ( "github.com/hyperledger/fabric/core/common/ccprovider" "github.com/hyperledger/fabric/core/common/sysccprovider" cutil "github.com/hyperledger/fabric/core/container/util" + "github.com/hyperledger/fabric/core/ledger/cceventmgmt" "github.com/hyperledger/fabric/core/mocks/scc/lscc" "github.com/hyperledger/fabric/core/policy" policymocks "github.com/hyperledger/fabric/core/policy/mocks" @@ -84,6 +85,11 @@ func constructDeploymentSpec(name string, path string, version string, initArgs //TestInstall tests the install function with various inputs func TestInstall(t *testing.T) { + + // Initialize cceventmgmt Mgr + // TODO cceventmgmt singleton should be refactored out of peer in the future. See CR 16549 for details. + cceventmgmt.Initialize() + scc := &lifeCycleSysCC{support: &lscc.MockSupport{}} stub := shim.NewMockStub("lscc", scc) res := stub.MockInit("1", nil)