forked from hyperledger/fabric
-
Notifications
You must be signed in to change notification settings - Fork 0
/
value_encoding.go
95 lines (87 loc) · 3.61 KB
/
value_encoding.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package stateleveldb
import (
proto "github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/statedb"
"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/statedb/stateleveldb/msgs"
"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/version"
)
// encode value encodes the versioned value. starting in v1.3 the encoding begins with a nil
// byte and includes metadata.
func encodeValue(v *statedb.VersionedValue) ([]byte, error) {
vvMsg := &msgs.VersionedValueProto{
VersionBytes: v.Version.ToBytes(),
Value: v.Value,
Metadata: v.Metadata,
}
encodedValue, err := proto.Marshal(vvMsg)
if err != nil {
return nil, err
}
encodedValue = append([]byte{0}, encodedValue...)
return encodedValue, nil
}
// decodeValue decodes the statedb value bytes using either the old (pre-v1.3) encoding
// or the new (v1.3 and later) encoding that supports metadata.
func decodeValue(encodedValue []byte) (*statedb.VersionedValue, error) {
if oldFormatEncoding(encodedValue) {
val, ver, err := decodeValueOldFormat(encodedValue)
if err != nil {
return nil, err
}
return &statedb.VersionedValue{Version: ver, Value: val, Metadata: nil}, nil
}
msg := &msgs.VersionedValueProto{}
err := proto.Unmarshal(encodedValue[1:], msg)
if err != nil {
return nil, err
}
ver, _, err := version.NewHeightFromBytes(msg.VersionBytes)
if err != nil {
return nil, err
}
val := msg.Value
metadata := msg.Metadata
// protobuf always makes an empty byte array as nil
if val == nil {
val = []byte{}
}
return &statedb.VersionedValue{Version: ver, Value: val, Metadata: metadata}, nil
}
// encodeValueOldFormat appends the value to the version, allows storage of version and value in binary form.
// With the introduction of metadata feature in v1.3, we change the encoding (see function below). However, we retain
// this function for test so as to make sure that we can decode old format and support mixed formats present
// in a statedb. This function should be used only in tests to generate the encoding in old format
func encodeValueOldFormat(value []byte, version *version.Height) []byte {
encodedValue := version.ToBytes()
if value != nil {
encodedValue = append(encodedValue, value...)
}
return encodedValue
}
// decodeValueOldFormat separates the version and value from a binary value
// See comments in the function `encodeValueOldFormat`. We retain this function as is
// to use this for decoding the old format (pre-v1.3) data present in the statedb. This function
// should not be used directly or in a tests. The function 'decodeValue' should be used
// for all decodings - which is expected to detect the encoded format and direct the call
// to this function for decoding the values encoded in the old format
func decodeValueOldFormat(encodedValue []byte) ([]byte, *version.Height, error) {
height, n, err := version.NewHeightFromBytes(encodedValue)
if err != nil {
return nil, nil, err
}
value := encodedValue[n:]
return value, height, nil
}
// oldFormatEncoding checks whether the value is encoded using the old (pre-v1.3) format
// or new format (v1.3 and later for encoding metadata).
func oldFormatEncoding(encodedValue []byte) bool {
return encodedValue[0] != byte(0) ||
(encodedValue[0]|encodedValue[1]) == byte(0) // this check covers a corner case
// where the old formatted value happens to start with a nil byte. In this corner case,
// the channel config happen to be persisted for the tuple <block 0, tran 0>. So, this
// is assumed that block 0 contains a single transaction (i.e., tran 0)
}