github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/ledger/kvledger/txmgmt/statedb/statecouchdb/couchdoc_conv.go (about)

     1  /*
     2  Copyright hechain. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package statecouchdb
     8  
     9  import (
    10  	"bytes"
    11  	"encoding/json"
    12  	"fmt"
    13  	"strings"
    14  	"unicode/utf8"
    15  
    16  	"github.com/hechain20/hechain/core/ledger/internal/version"
    17  	"github.com/hechain20/hechain/core/ledger/kvledger/txmgmt/statedb"
    18  	"github.com/pkg/errors"
    19  )
    20  
    21  const (
    22  	binaryWrapper = "valueBytes"
    23  	idField       = "_id"
    24  	revField      = "_rev"
    25  	versionField  = "~version"
    26  	deletedField  = "_deleted"
    27  )
    28  
    29  type keyValue struct {
    30  	key      string
    31  	revision string
    32  	*statedb.VersionedValue
    33  }
    34  
    35  type jsonValue map[string]interface{}
    36  
    37  func tryCastingToJSON(b []byte) (isJSON bool, val jsonValue) {
    38  	var jsonVal map[string]interface{}
    39  	err := json.Unmarshal(b, &jsonVal)
    40  	return err == nil, jsonValue(jsonVal)
    41  }
    42  
    43  func castToJSON(b []byte) (jsonValue, error) {
    44  	var jsonVal map[string]interface{}
    45  	err := json.Unmarshal(b, &jsonVal)
    46  	err = errors.Wrap(err, "error unmarshalling json data")
    47  	return jsonVal, err
    48  }
    49  
    50  func (v jsonValue) checkReservedFieldsNotPresent() error {
    51  	for fieldName := range v {
    52  		if fieldName == versionField || strings.HasPrefix(fieldName, "_") {
    53  			return errors.Errorf("field [%s] is not valid for the CouchDB state database", fieldName)
    54  		}
    55  	}
    56  	return nil
    57  }
    58  
    59  func (v jsonValue) removeRevField() {
    60  	delete(v, revField)
    61  }
    62  
    63  func (v jsonValue) toBytes() ([]byte, error) {
    64  	jsonBytes, err := json.Marshal(v)
    65  	err = errors.Wrap(err, "error marshalling json data")
    66  	return jsonBytes, err
    67  }
    68  
    69  func couchDocToKeyValue(doc *couchDoc) (*keyValue, error) {
    70  	docFields, err := validateAndRetrieveFields(doc)
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  	version, metadata, err := decodeVersionAndMetadata(docFields.versionAndMetadata)
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  	return &keyValue{
    79  		docFields.id, docFields.revision,
    80  		&statedb.VersionedValue{
    81  			Value:    docFields.value,
    82  			Version:  version,
    83  			Metadata: metadata,
    84  		},
    85  	}, nil
    86  }
    87  
    88  type couchDocFields struct {
    89  	id                 string
    90  	revision           string
    91  	value              []byte
    92  	versionAndMetadata string
    93  }
    94  
    95  func validateAndRetrieveFields(doc *couchDoc) (*couchDocFields, error) {
    96  	jsonDoc := make(jsonValue)
    97  	decoder := json.NewDecoder(bytes.NewBuffer(doc.jsonValue))
    98  	decoder.UseNumber()
    99  	if err := decoder.Decode(&jsonDoc); err != nil {
   100  		return nil, err
   101  	}
   102  
   103  	docFields := &couchDocFields{}
   104  	docFields.id = jsonDoc[idField].(string)
   105  	if jsonDoc[revField] != nil {
   106  		docFields.revision = jsonDoc[revField].(string)
   107  	}
   108  	if jsonDoc[versionField] == nil {
   109  		return nil, fmt.Errorf("version field %s was not found", versionField)
   110  	}
   111  	docFields.versionAndMetadata = jsonDoc[versionField].(string)
   112  
   113  	delete(jsonDoc, idField)
   114  	delete(jsonDoc, revField)
   115  	delete(jsonDoc, versionField)
   116  
   117  	var err error
   118  	if doc.attachments == nil {
   119  		docFields.value, err = json.Marshal(jsonDoc)
   120  		return docFields, err
   121  	}
   122  	for _, attachment := range doc.attachments {
   123  		if attachment.Name == binaryWrapper {
   124  			docFields.value = attachment.AttachmentBytes
   125  		}
   126  	}
   127  	return docFields, err
   128  }
   129  
   130  func keyValToCouchDoc(kv *keyValue) (*couchDoc, error) {
   131  	type kvType int32
   132  	const (
   133  		kvTypeDelete = iota
   134  		kvTypeJSON
   135  		kvTypeAttachment
   136  	)
   137  	key, value, metadata, version := kv.key, kv.Value, kv.Metadata, kv.Version
   138  	jsonMap := make(jsonValue)
   139  
   140  	var kvtype kvType
   141  	switch {
   142  	case value == nil:
   143  		kvtype = kvTypeDelete
   144  	// check for the case where the jsonMap is nil,  this will indicate
   145  	// a special case for the Unmarshal that results in a valid JSON returning nil
   146  	case json.Unmarshal(value, &jsonMap) == nil && jsonMap != nil:
   147  		kvtype = kvTypeJSON
   148  		if err := jsonMap.checkReservedFieldsNotPresent(); err != nil {
   149  			return nil, err
   150  		}
   151  	default:
   152  		// create an empty map, if the map is nil
   153  		if jsonMap == nil {
   154  			jsonMap = make(jsonValue)
   155  		}
   156  		kvtype = kvTypeAttachment
   157  	}
   158  
   159  	verAndMetadata, err := encodeVersionAndMetadata(version, metadata)
   160  	if err != nil {
   161  		return nil, err
   162  	}
   163  	// add the (version + metadata), id, revision, and delete marker (if needed)
   164  	jsonMap[versionField] = verAndMetadata
   165  	jsonMap[idField] = key
   166  	if kv.revision != "" {
   167  		jsonMap[revField] = kv.revision
   168  	}
   169  	if kvtype == kvTypeDelete {
   170  		jsonMap[deletedField] = true
   171  	}
   172  	jsonBytes, err := jsonMap.toBytes()
   173  	if err != nil {
   174  		return nil, err
   175  	}
   176  	couchDoc := &couchDoc{jsonValue: jsonBytes}
   177  	if kvtype == kvTypeAttachment {
   178  		attachment := &attachmentInfo{}
   179  		attachment.AttachmentBytes = value
   180  		attachment.ContentType = "application/octet-stream"
   181  		attachment.Name = binaryWrapper
   182  		attachments := append([]*attachmentInfo{}, attachment)
   183  		couchDoc.attachments = attachments
   184  	}
   185  	return couchDoc, nil
   186  }
   187  
   188  // couchSavepointData data for couchdb
   189  type couchSavepointData struct {
   190  	BlockNum uint64 `json:"BlockNum"`
   191  	TxNum    uint64 `json:"TxNum"`
   192  }
   193  
   194  type channelMetadata struct {
   195  	ChannelName string `json:"ChannelName"`
   196  	// namespace to namespaceDBInfo mapping
   197  	NamespaceDBsInfo map[string]*namespaceDBInfo `json:"NamespaceDBsInfo"`
   198  }
   199  
   200  type namespaceDBInfo struct {
   201  	Namespace string `json:"Namespace"`
   202  	DBName    string `json:"DBName"`
   203  }
   204  
   205  func encodeSavepoint(height *version.Height) (*couchDoc, error) {
   206  	var err error
   207  	var savepointDoc couchSavepointData
   208  	// construct savepoint document
   209  	savepointDoc.BlockNum = height.BlockNum
   210  	savepointDoc.TxNum = height.TxNum
   211  	savepointDocJSON, err := json.Marshal(savepointDoc)
   212  	if err != nil {
   213  		err = errors.Wrap(err, "failed to marshal savepoint data")
   214  		logger.Errorf("%+v", err)
   215  		return nil, err
   216  	}
   217  	return &couchDoc{jsonValue: savepointDocJSON, attachments: nil}, nil
   218  }
   219  
   220  func decodeSavepoint(couchDoc *couchDoc) (*version.Height, error) {
   221  	savepointDoc := &couchSavepointData{}
   222  	if err := json.Unmarshal(couchDoc.jsonValue, &savepointDoc); err != nil {
   223  		err = errors.Wrap(err, "failed to unmarshal savepoint data")
   224  		logger.Errorf("%+v", err)
   225  		return nil, err
   226  	}
   227  	return &version.Height{BlockNum: savepointDoc.BlockNum, TxNum: savepointDoc.TxNum}, nil
   228  }
   229  
   230  func encodeChannelMetadata(metadataDoc *channelMetadata) (*couchDoc, error) {
   231  	metadataJSON, err := json.Marshal(metadataDoc)
   232  	if err != nil {
   233  		err = errors.Wrap(err, "failed to marshal channel metadata")
   234  		logger.Errorf("%+v", err)
   235  		return nil, err
   236  	}
   237  	return &couchDoc{jsonValue: metadataJSON, attachments: nil}, nil
   238  }
   239  
   240  func decodeChannelMetadata(couchDoc *couchDoc) (*channelMetadata, error) {
   241  	metadataDoc := &channelMetadata{}
   242  	if err := json.Unmarshal(couchDoc.jsonValue, &metadataDoc); err != nil {
   243  		err = errors.Wrap(err, "failed to unmarshal channel metadata")
   244  		logger.Errorf("%+v", err)
   245  		return nil, err
   246  	}
   247  	return metadataDoc, nil
   248  }
   249  
   250  type dataformatInfo struct {
   251  	Version string `json:"Version"`
   252  }
   253  
   254  func encodeDataformatInfo(dataFormatVersion string) (*couchDoc, error) {
   255  	var err error
   256  	dataformatInfo := &dataformatInfo{
   257  		Version: dataFormatVersion,
   258  	}
   259  	dataformatInfoJSON, err := json.Marshal(dataformatInfo)
   260  	if err != nil {
   261  		err = errors.Wrapf(err, "failed to marshal dataformatInfo [%#v]", dataformatInfo)
   262  		logger.Errorf("%+v", err)
   263  		return nil, err
   264  	}
   265  	return &couchDoc{jsonValue: dataformatInfoJSON, attachments: nil}, nil
   266  }
   267  
   268  func decodeDataformatInfo(couchDoc *couchDoc) (string, error) {
   269  	dataformatInfo := &dataformatInfo{}
   270  	if err := json.Unmarshal(couchDoc.jsonValue, dataformatInfo); err != nil {
   271  		err = errors.Wrapf(err, "failed to unmarshal json [%#v] into dataformatInfo", couchDoc.jsonValue)
   272  		logger.Errorf("%+v", err)
   273  		return "", err
   274  	}
   275  	return dataformatInfo.Version, nil
   276  }
   277  
   278  func validateValue(value []byte) error {
   279  	isJSON, jsonVal := tryCastingToJSON(value)
   280  	if !isJSON {
   281  		return nil
   282  	}
   283  	return jsonVal.checkReservedFieldsNotPresent()
   284  }
   285  
   286  func validateKey(key string) error {
   287  	if !utf8.ValidString(key) {
   288  		return errors.Errorf("invalid key [%x], must be a UTF-8 string", key)
   289  	}
   290  	if strings.HasPrefix(key, "_") {
   291  		return errors.Errorf("invalid key [%s], cannot begin with \"_\"", key)
   292  	}
   293  	if key == "" {
   294  		return errors.New("invalid key. Empty string is not supported as a key by couchdb")
   295  	}
   296  	return nil
   297  }
   298  
   299  // removeJSONRevision removes the "_rev" if this is a JSON
   300  func removeJSONRevision(jsonValue *[]byte) error {
   301  	jsonVal, err := castToJSON(*jsonValue)
   302  	if err != nil {
   303  		logger.Errorf("Failed to unmarshal couchdb JSON data: %+v", err)
   304  		return err
   305  	}
   306  	jsonVal.removeRevField()
   307  	if *jsonValue, err = jsonVal.toBytes(); err != nil {
   308  		logger.Errorf("Failed to marshal couchdb JSON data: %+v", err)
   309  	}
   310  	return err
   311  }