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 }