github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/ledger/confighistory/mgr.go (about)

     1  /*
     2  Copyright hechain. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package confighistory
     8  
     9  import (
    10  	"fmt"
    11  	"path/filepath"
    12  
    13  	"github.com/golang/protobuf/proto"
    14  	"github.com/hechain20/hechain/common/flogging"
    15  	"github.com/hechain20/hechain/common/ledger/snapshot"
    16  	"github.com/hechain20/hechain/core/ledger"
    17  	"github.com/hechain20/hechain/internal/fileutil"
    18  	"github.com/hyperledger/fabric-protos-go/ledger/rwset/kvrwset"
    19  	"github.com/hyperledger/fabric-protos-go/peer"
    20  	"github.com/pkg/errors"
    21  )
    22  
    23  var (
    24  	logger                 = flogging.MustGetLogger("confighistory")
    25  	importConfigsBatchSize = 1024 * 1024
    26  )
    27  
    28  const (
    29  	collectionConfigNamespace = "lscc" // lscc namespace was introduced in version 1.2 and we continue to use this in order to be compatible with existing data
    30  	snapshotFileFormat        = byte(1)
    31  	snapshotDataFileName      = "confighistory.data"
    32  	snapshotMetadataFileName  = "confighistory.metadata"
    33  )
    34  
    35  // Mgr manages the history of configurations such as chaincode's collection configurations.
    36  // It should be registered as a state listener. The state listener builds the history.
    37  type Mgr struct {
    38  	ccInfoProvider ledger.DeployedChaincodeInfoProvider
    39  	dbProvider     *dbProvider
    40  }
    41  
    42  // NewMgr constructs an instance that implements interface `Mgr`
    43  func NewMgr(dbPath string, ccInfoProvider ledger.DeployedChaincodeInfoProvider) (*Mgr, error) {
    44  	p, err := newDBProvider(dbPath)
    45  	if err != nil {
    46  		return nil, err
    47  	}
    48  	return &Mgr{ccInfoProvider, p}, nil
    49  }
    50  
    51  // Name returns the name of the listener
    52  func (m *Mgr) Name() string {
    53  	return "collection configuration history listener"
    54  }
    55  
    56  // Initialize implements function from the interface ledger.StateListener
    57  func (m *Mgr) Initialize(ledgerID string, qe ledger.SimpleQueryExecutor) error {
    58  	// Noop
    59  	return nil
    60  }
    61  
    62  // InterestedInNamespaces implements function from the interface ledger.StateListener
    63  func (m *Mgr) InterestedInNamespaces() []string {
    64  	return m.ccInfoProvider.Namespaces()
    65  }
    66  
    67  // StateCommitDone implements function from the interface ledger.StateListener
    68  func (m *Mgr) StateCommitDone(ledgerID string) {
    69  	// Noop
    70  }
    71  
    72  // HandleStateUpdates implements function from the interface ledger.StateListener
    73  // In this implementation, the latest collection config package is retrieved via
    74  // ledger.DeployedChaincodeInfoProvider and is persisted as a separate entry in a separate db.
    75  // The composite key for the entry is a tuple of <blockNum, namespace, key>
    76  func (m *Mgr) HandleStateUpdates(trigger *ledger.StateUpdateTrigger) error {
    77  	updatedCCs, err := m.ccInfoProvider.UpdatedChaincodes(extractPublicUpdates(trigger.StateUpdates))
    78  	if err != nil {
    79  		return err
    80  	}
    81  	// updated chaincodes can be empty if the invocation to this function is triggered
    82  	// because of state updates that contains only chaincode approval transaction output
    83  	if len(updatedCCs) == 0 {
    84  		return nil
    85  	}
    86  	updatedCollConfigs := map[string]*peer.CollectionConfigPackage{}
    87  	for _, cc := range updatedCCs {
    88  		ccInfo, err := m.ccInfoProvider.ChaincodeInfo(trigger.LedgerID, cc.Name, trigger.PostCommitQueryExecutor)
    89  		if err != nil {
    90  			return err
    91  		}
    92  
    93  		// DeployedChaincodeInfoProvider implementation in new lifecycle return an empty 'CollectionConfigPackage'
    94  		// (instead of a nil) to indicate the absence of collection config, so check for both conditions
    95  		if ccInfo.ExplicitCollectionConfigPkg == nil || len(ccInfo.ExplicitCollectionConfigPkg.Config) == 0 {
    96  			continue
    97  		}
    98  		updatedCollConfigs[ccInfo.Name] = ccInfo.ExplicitCollectionConfigPkg
    99  	}
   100  	if len(updatedCollConfigs) == 0 {
   101  		return nil
   102  	}
   103  	dbHandle := m.dbProvider.getDB(trigger.LedgerID)
   104  	batch := dbHandle.newBatch()
   105  	err = prepareDBBatch(batch, updatedCollConfigs, trigger.CommittingBlockNum)
   106  	if err != nil {
   107  		return err
   108  	}
   109  	return dbHandle.writeBatch(batch, true)
   110  }
   111  
   112  // ImportConfigHistory imports the collection config history associated with a given
   113  // ledgerID from the snapshot files present in the dir
   114  func (m *Mgr) ImportFromSnapshot(ledgerID string, dir string) error {
   115  	exist, _, err := fileutil.FileExists(filepath.Join(dir, snapshotDataFileName))
   116  	if err != nil {
   117  		return err
   118  	}
   119  	if !exist {
   120  		// when the ledger being bootstapped never had a private data collection for
   121  		// any chaincode, the snapshot files associated with the confighistory store
   122  		// will not be present in the snapshot directory. Hence, we can return early
   123  		return nil
   124  	}
   125  	db := m.dbProvider.getDB(ledgerID)
   126  	empty, err := db.IsEmpty()
   127  	if err != nil {
   128  		return err
   129  	}
   130  	if !empty {
   131  		return errors.New(fmt.Sprintf(
   132  			"config history for ledger [%s] exists. Incremental import is not supported. "+
   133  				"Remove the existing ledger data before retry",
   134  			ledgerID,
   135  		))
   136  	}
   137  
   138  	configMetadata, err := snapshot.OpenFile(filepath.Join(dir, snapshotMetadataFileName), snapshotFileFormat)
   139  	if err != nil {
   140  		return err
   141  	}
   142  	numCollectionConfigs, err := configMetadata.DecodeUVarInt()
   143  	if err != nil {
   144  		return err
   145  	}
   146  	collectionConfigData, err := snapshot.OpenFile(filepath.Join(dir, snapshotDataFileName), snapshotFileFormat)
   147  	if err != nil {
   148  		return err
   149  	}
   150  
   151  	batch := db.NewUpdateBatch()
   152  	currentBatchSize := 0
   153  	for i := uint64(0); i < numCollectionConfigs; i++ {
   154  		key, err := collectionConfigData.DecodeBytes()
   155  		if err != nil {
   156  			return err
   157  		}
   158  		val, err := collectionConfigData.DecodeBytes()
   159  		if err != nil {
   160  			return err
   161  		}
   162  		batch.Put(key, val)
   163  		currentBatchSize += len(key) + len(val)
   164  		if currentBatchSize >= importConfigsBatchSize {
   165  			if err := db.WriteBatch(batch, true); err != nil {
   166  				return err
   167  			}
   168  			currentBatchSize = 0
   169  			batch.Reset()
   170  		}
   171  	}
   172  	return db.WriteBatch(batch, true)
   173  }
   174  
   175  // GetRetriever returns an implementation of `ledger.ConfigHistoryRetriever` for the given ledger id.
   176  func (m *Mgr) GetRetriever(ledgerID string) *Retriever {
   177  	return &Retriever{
   178  		ledgerID: ledgerID,
   179  		dbHandle: m.dbProvider.getDB(ledgerID),
   180  	}
   181  }
   182  
   183  // Close implements the function in the interface 'Mgr'
   184  func (m *Mgr) Close() {
   185  	m.dbProvider.Close()
   186  }
   187  
   188  // Drop drops channel-specific data from the config history db
   189  func (m *Mgr) Drop(ledgerid string) error {
   190  	return m.dbProvider.Drop(ledgerid)
   191  }
   192  
   193  // Retriever helps consumer retrieve collection config history
   194  type Retriever struct {
   195  	ledgerID string
   196  	dbHandle *db
   197  }
   198  
   199  // MostRecentCollectionConfigBelow implements function from the interface ledger.ConfigHistoryRetriever
   200  func (r *Retriever) MostRecentCollectionConfigBelow(blockNum uint64, chaincodeName string) (*ledger.CollectionConfigInfo, error) {
   201  	compositeKV, err := r.dbHandle.mostRecentEntryBelow(blockNum, collectionConfigNamespace, constructCollectionConfigKey(chaincodeName))
   202  	if err != nil || compositeKV == nil {
   203  		return nil, err
   204  	}
   205  	return compositeKVToCollectionConfig(compositeKV)
   206  }
   207  
   208  // ExportConfigHistory exports configuration history from the confighistoryDB to
   209  // a file. Currently, we store only one type of configuration in the db, i.e.,
   210  // private data collection configuration.
   211  // We write the full key and value stored in the database as is to the file.
   212  // Though we could decode the key and write a proto message with exact ns, key,
   213  // block number, and collection config, we store the full key and value to avoid
   214  // unnecessary encoding and decoding of proto messages.
   215  // The key format stored in db is "s" + ns + byte(0) + key + "~collection" + byte(0)
   216  // + blockNum. As we store the key as is, we store 13 extra bytes. For a million
   217  // records, it would add only 12 MB overhead. Note that the protobuf also adds some
   218  // extra bytes. Further, the collection config namespace is not expected to have
   219  // millions of entries.
   220  func (r *Retriever) ExportConfigHistory(dir string, newHashFunc snapshot.NewHashFunc) (map[string][]byte, error) {
   221  	nsItr, err := r.dbHandle.getNamespaceIterator(collectionConfigNamespace)
   222  	if err != nil {
   223  		return nil, err
   224  	}
   225  	defer nsItr.Release()
   226  
   227  	var numCollectionConfigs uint64 = 0
   228  	var dataFileWriter *snapshot.FileWriter
   229  	for nsItr.Next() {
   230  		if err := nsItr.Error(); err != nil {
   231  			return nil, errors.Wrap(err, "internal leveldb error while iterating for collection config history")
   232  		}
   233  		if numCollectionConfigs == 0 { // first iteration, create the data file
   234  			dataFileWriter, err = snapshot.CreateFile(filepath.Join(dir, snapshotDataFileName), snapshotFileFormat, newHashFunc)
   235  			if err != nil {
   236  				return nil, err
   237  			}
   238  			defer dataFileWriter.Close()
   239  		}
   240  		if err := dataFileWriter.EncodeBytes(nsItr.Key()); err != nil {
   241  			return nil, err
   242  		}
   243  		if err := dataFileWriter.EncodeBytes(nsItr.Value()); err != nil {
   244  			return nil, err
   245  		}
   246  		numCollectionConfigs++
   247  	}
   248  
   249  	if dataFileWriter == nil {
   250  		return nil, nil
   251  	}
   252  
   253  	dataHash, err := dataFileWriter.Done()
   254  	if err != nil {
   255  		return nil, err
   256  	}
   257  	metadataFileWriter, err := snapshot.CreateFile(filepath.Join(dir, snapshotMetadataFileName), snapshotFileFormat, newHashFunc)
   258  	if err != nil {
   259  		return nil, err
   260  	}
   261  	defer metadataFileWriter.Close()
   262  	if err = metadataFileWriter.EncodeUVarint(numCollectionConfigs); err != nil {
   263  		return nil, err
   264  	}
   265  	metadataHash, err := metadataFileWriter.Done()
   266  	if err != nil {
   267  		return nil, err
   268  	}
   269  
   270  	return map[string][]byte{
   271  		snapshotDataFileName:     dataHash,
   272  		snapshotMetadataFileName: metadataHash,
   273  	}, nil
   274  }
   275  
   276  func prepareDBBatch(batch *batch, chaincodeCollConfigs map[string]*peer.CollectionConfigPackage, committingBlockNum uint64) error {
   277  	for ccName, collConfig := range chaincodeCollConfigs {
   278  		key := constructCollectionConfigKey(ccName)
   279  		var configBytes []byte
   280  		var err error
   281  		if configBytes, err = proto.Marshal(collConfig); err != nil {
   282  			return errors.WithStack(err)
   283  		}
   284  		batch.add(collectionConfigNamespace, key, committingBlockNum, configBytes)
   285  	}
   286  	return nil
   287  }
   288  
   289  func compositeKVToCollectionConfig(compositeKV *compositeKV) (*ledger.CollectionConfigInfo, error) {
   290  	conf := &peer.CollectionConfigPackage{}
   291  	if err := proto.Unmarshal(compositeKV.value, conf); err != nil {
   292  		return nil, errors.Wrap(err, "error unmarshalling compositeKV to collection config")
   293  	}
   294  	return &ledger.CollectionConfigInfo{
   295  		CollectionConfig:   conf,
   296  		CommittingBlockNum: compositeKV.blockNum,
   297  	}, nil
   298  }
   299  
   300  func constructCollectionConfigKey(chaincodeName string) string {
   301  	return chaincodeName + "~collection" // collection config key as in version 1.2 and we continue to use this in order to be compatible with existing data
   302  }
   303  
   304  func extractPublicUpdates(stateUpdates ledger.StateUpdates) map[string][]*kvrwset.KVWrite {
   305  	m := map[string][]*kvrwset.KVWrite{}
   306  	for ns, updates := range stateUpdates {
   307  		m[ns] = updates.PublicUpdates
   308  	}
   309  	return m
   310  }