github.com/true-sqn/fabric@v2.1.1+incompatible/core/common/privdata/store.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package privdata
     8  
     9  import (
    10  	"fmt"
    11  
    12  	"github.com/golang/protobuf/proto"
    13  	"github.com/hyperledger/fabric-protos-go/peer"
    14  	pb "github.com/hyperledger/fabric-protos-go/peer"
    15  	"github.com/hyperledger/fabric/core/ledger"
    16  	"github.com/hyperledger/fabric/msp"
    17  	mspmgmt "github.com/hyperledger/fabric/msp/mgmt"
    18  	"github.com/hyperledger/fabric/protoutil"
    19  	"github.com/pkg/errors"
    20  )
    21  
    22  // State retrieves data from the state.
    23  type State interface {
    24  	// GetState retrieves the value for the given key in the given namespace
    25  	GetState(namespace string, key string) ([]byte, error)
    26  }
    27  
    28  type NoSuchCollectionError CollectionCriteria
    29  
    30  func (f NoSuchCollectionError) Error() string {
    31  	return fmt.Sprintf("collection %s/%s/%s could not be found", f.Channel, f.Namespace, f.Collection)
    32  }
    33  
    34  // A QueryExecutorFactory is responsible for creating ledger.QueryExectuor
    35  // instances.
    36  type QueryExecutorFactory interface {
    37  	NewQueryExecutor() (ledger.QueryExecutor, error)
    38  }
    39  
    40  // ChaincodeInfoProvider provides information about deployed chaincode.
    41  // LSCC module is expected to provide an implementation for this dependencys
    42  type ChaincodeInfoProvider interface {
    43  	// ChaincodeInfo returns the info about a deployed chaincode.
    44  	ChaincodeInfo(channelName, chaincodeName string, qe ledger.SimpleQueryExecutor) (*ledger.DeployedChaincodeInfo, error)
    45  	// CollectionInfo returns the proto msg that defines the named collection.
    46  	// This function can be used for both explicit and implicit collections.
    47  	CollectionInfo(channelName, chaincodeName, collectionName string, qe ledger.SimpleQueryExecutor) (*peer.StaticCollectionConfig, error)
    48  	// AllCollectionsConfigPkg returns a combined collection config pkg that contains both explicit and implicit collections
    49  	AllCollectionsConfigPkg(channelName, chaincodeName string, qe ledger.SimpleQueryExecutor) (*peer.CollectionConfigPackage, error)
    50  }
    51  
    52  // IdentityDeserializerFactory creates msp.IdentityDeserializer for
    53  // a chain.
    54  type IdentityDeserializerFactory interface {
    55  	GetIdentityDeserializer(chainID string) msp.IdentityDeserializer
    56  }
    57  
    58  // IdentityDeserializerFactoryFunc is a function adapater for
    59  // IdentityDeserializerFactory.
    60  type IdentityDeserializerFactoryFunc func(chainID string) msp.IdentityDeserializer
    61  
    62  func (i IdentityDeserializerFactoryFunc) GetIdentityDeserializer(chainID string) msp.IdentityDeserializer {
    63  	return i(chainID)
    64  }
    65  
    66  // CollectionCriteria defines an element of a private data that corresponds
    67  // to a certain transaction and collection
    68  type CollectionCriteria struct {
    69  	Channel    string
    70  	Collection string
    71  	Namespace  string
    72  }
    73  
    74  type SimpleCollectionStore struct {
    75  	qeFactory             QueryExecutorFactory
    76  	ccInfoProvider        ChaincodeInfoProvider
    77  	idDeserializerFactory IdentityDeserializerFactory
    78  }
    79  
    80  func NewSimpleCollectionStore(qeFactory QueryExecutorFactory, ccInfoProvider ChaincodeInfoProvider) *SimpleCollectionStore {
    81  	return &SimpleCollectionStore{
    82  		qeFactory:      qeFactory,
    83  		ccInfoProvider: ccInfoProvider,
    84  		idDeserializerFactory: IdentityDeserializerFactoryFunc(func(chainID string) msp.IdentityDeserializer {
    85  			return mspmgmt.GetManagerForChain(chainID)
    86  		}),
    87  	}
    88  }
    89  
    90  func (c *SimpleCollectionStore) retrieveCollectionConfigPackage(cc CollectionCriteria, qe ledger.QueryExecutor) (*peer.CollectionConfigPackage, error) {
    91  	var err error
    92  	if qe == nil {
    93  		qe, err = c.qeFactory.NewQueryExecutor()
    94  		if err != nil {
    95  			return nil, errors.WithMessagef(err, "could not retrieve query executor for collection criteria %#v", cc)
    96  		}
    97  		defer qe.Done()
    98  	}
    99  	return c.ccInfoProvider.AllCollectionsConfigPkg(cc.Channel, cc.Namespace, qe)
   100  }
   101  
   102  // RetrieveCollectionConfigPackageFromState retrieves the collection config package from the given key from the given state
   103  func RetrieveCollectionConfigPackageFromState(cc CollectionCriteria, state State) (*peer.CollectionConfigPackage, error) {
   104  	cb, err := state.GetState("lscc", BuildCollectionKVSKey(cc.Namespace))
   105  	if err != nil {
   106  		return nil, errors.WithMessagef(err, "error while retrieving collection for collection criteria %#v", cc)
   107  	}
   108  	if cb == nil {
   109  		return nil, NoSuchCollectionError(cc)
   110  	}
   111  	conf, err := ParseCollectionConfig(cb)
   112  	if err != nil {
   113  		return nil, errors.Wrapf(err, "invalid configuration for collection criteria %#v", cc)
   114  	}
   115  	return conf, nil
   116  }
   117  
   118  // ParseCollectionConfig parses the collection configuration from the given serialized representation.
   119  func ParseCollectionConfig(colBytes []byte) (*peer.CollectionConfigPackage, error) {
   120  	collections := &peer.CollectionConfigPackage{}
   121  	err := proto.Unmarshal(colBytes, collections)
   122  	if err != nil {
   123  		return nil, errors.WithStack(err)
   124  	}
   125  
   126  	return collections, nil
   127  }
   128  
   129  // RetrieveCollectionConfig retrieves a collection's config
   130  func (c *SimpleCollectionStore) RetrieveCollectionConfig(cc CollectionCriteria) (*peer.StaticCollectionConfig, error) {
   131  	return c.retrieveCollectionConfig(cc, nil)
   132  }
   133  
   134  func (c *SimpleCollectionStore) retrieveCollectionConfig(cc CollectionCriteria, qe ledger.QueryExecutor) (*peer.StaticCollectionConfig, error) {
   135  	var err error
   136  	if qe == nil {
   137  		qe, err = c.qeFactory.NewQueryExecutor()
   138  		if err != nil {
   139  			return nil, errors.WithMessagef(err, "could not retrieve query executor for collection criteria %#v", cc)
   140  		}
   141  		defer qe.Done()
   142  	}
   143  	collConfig, err := c.ccInfoProvider.CollectionInfo(cc.Channel, cc.Namespace, cc.Collection, qe)
   144  	if err != nil {
   145  		return nil, err
   146  	}
   147  	if collConfig == nil {
   148  		return nil, NoSuchCollectionError(cc)
   149  	}
   150  	return collConfig, nil
   151  }
   152  
   153  func (c *SimpleCollectionStore) retrieveSimpleCollection(cc CollectionCriteria, qe ledger.QueryExecutor) (*SimpleCollection, error) {
   154  	staticCollectionConfig, err := c.retrieveCollectionConfig(cc, qe)
   155  	if err != nil {
   156  		return nil, err
   157  	}
   158  	sc := &SimpleCollection{}
   159  	err = sc.Setup(staticCollectionConfig, c.idDeserializerFactory.GetIdentityDeserializer(cc.Channel))
   160  	if err != nil {
   161  		return nil, errors.WithMessagef(err, "error setting up collection for collection criteria %#v", cc)
   162  	}
   163  	return sc, nil
   164  }
   165  
   166  func (c *SimpleCollectionStore) AccessFilter(channelName string, collectionPolicyConfig *peer.CollectionPolicyConfig) (Filter, error) {
   167  	sc := &SimpleCollection{}
   168  	err := sc.setupAccessPolicy(collectionPolicyConfig, c.idDeserializerFactory.GetIdentityDeserializer(channelName))
   169  	if err != nil {
   170  		return nil, err
   171  	}
   172  	return sc.AccessFilter(), nil
   173  }
   174  
   175  func (c *SimpleCollectionStore) RetrieveCollection(cc CollectionCriteria) (Collection, error) {
   176  	return c.retrieveSimpleCollection(cc, nil)
   177  }
   178  
   179  func (c *SimpleCollectionStore) RetrieveCollectionAccessPolicy(cc CollectionCriteria) (CollectionAccessPolicy, error) {
   180  	return c.retrieveSimpleCollection(cc, nil)
   181  }
   182  
   183  func (c *SimpleCollectionStore) RetrieveCollectionConfigPackage(cc CollectionCriteria) (*peer.CollectionConfigPackage, error) {
   184  	return c.retrieveCollectionConfigPackage(cc, nil)
   185  }
   186  
   187  // RetrieveCollectionPersistenceConfigs retrieves the collection's persistence related configurations
   188  func (c *SimpleCollectionStore) RetrieveCollectionPersistenceConfigs(cc CollectionCriteria) (CollectionPersistenceConfigs, error) {
   189  	staticCollectionConfig, err := c.retrieveCollectionConfig(cc, nil)
   190  	if err != nil {
   191  		return nil, err
   192  	}
   193  	return &SimpleCollectionPersistenceConfigs{staticCollectionConfig.BlockToLive}, nil
   194  }
   195  
   196  // RetrieveReadWritePermission retrieves the read-write persmission of the creator of the
   197  // signedProposal for a given collection using collection access policy and flags such as
   198  // memberOnlyRead & memberOnlyWrite
   199  func (c *SimpleCollectionStore) RetrieveReadWritePermission(
   200  	cc CollectionCriteria,
   201  	signedProposal *pb.SignedProposal,
   202  	qe ledger.QueryExecutor,
   203  ) (bool, bool, error) {
   204  	collection, err := c.retrieveSimpleCollection(cc, qe)
   205  	if err != nil {
   206  		return false, false, err
   207  	}
   208  
   209  	if canAnyoneReadAndWrite(collection) {
   210  		return true, true, nil
   211  	}
   212  
   213  	// all members have read-write persmission
   214  	if isAMember, err := isCreatorOfProposalAMember(signedProposal, collection); err != nil {
   215  		return false, false, err
   216  	} else if isAMember {
   217  		return true, true, nil
   218  	}
   219  
   220  	return !collection.IsMemberOnlyRead(), !collection.IsMemberOnlyWrite(), nil
   221  }
   222  
   223  func canAnyoneReadAndWrite(collection *SimpleCollection) bool {
   224  	if !collection.IsMemberOnlyRead() && !collection.IsMemberOnlyWrite() {
   225  		return true
   226  	}
   227  	return false
   228  }
   229  
   230  func isCreatorOfProposalAMember(signedProposal *pb.SignedProposal, collection *SimpleCollection) (bool, error) {
   231  	signedData, err := getSignedData(signedProposal)
   232  	if err != nil {
   233  		return false, err
   234  	}
   235  
   236  	accessFilter := collection.AccessFilter()
   237  	return accessFilter(signedData), nil
   238  }
   239  
   240  func getSignedData(signedProposal *pb.SignedProposal) (protoutil.SignedData, error) {
   241  	proposal, err := protoutil.UnmarshalProposal(signedProposal.ProposalBytes)
   242  	if err != nil {
   243  		return protoutil.SignedData{}, err
   244  	}
   245  
   246  	hdr, err := protoutil.UnmarshalHeader(proposal.Header)
   247  	if err != nil {
   248  		return protoutil.SignedData{}, err
   249  	}
   250  
   251  	shdr, err := protoutil.UnmarshalSignatureHeader(hdr.SignatureHeader)
   252  	if err != nil {
   253  		return protoutil.SignedData{}, err
   254  	}
   255  
   256  	return protoutil.SignedData{
   257  		Data:      signedProposal.ProposalBytes,
   258  		Identity:  shdr.Creator,
   259  		Signature: signedProposal.Signature,
   260  	}, nil
   261  }