-
Notifications
You must be signed in to change notification settings - Fork 0
/
update.go
202 lines (165 loc) · 6.02 KB
/
update.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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package configtx
import (
"strings"
"github.com/hyperledger/fabric/common/policies"
cb "github.com/hyperledger/fabric/protos/common"
"github.com/pkg/errors"
)
func (vi *ValidatorImpl) verifyReadSet(readSet map[string]comparable) error {
for key, value := range readSet {
existing, ok := vi.configMap[key]
if !ok {
return errors.Errorf("existing config does not contain element for %s but was in the read set", key)
}
if existing.version() != value.version() {
return errors.Errorf("proposed update requires that key %s be at version %d, but it is currently at version %d", key, value.version(), existing.version())
}
}
return nil
}
func computeDeltaSet(readSet, writeSet map[string]comparable) map[string]comparable {
result := make(map[string]comparable)
for key, value := range writeSet {
readVal, ok := readSet[key]
if ok && readVal.version() == value.version() {
continue
}
// If the key in the readset is a different version, we include it
// Error checking on the sanity of the update is done against the config
result[key] = value
}
return result
}
func validateModPolicy(modPolicy string) error {
if modPolicy == "" {
return errors.Errorf("mod_policy not set")
}
trimmed := modPolicy
if modPolicy[0] == '/' {
trimmed = modPolicy[1:]
}
for i, pathElement := range strings.Split(trimmed, pathSeparator) {
err := validateConfigID(pathElement)
if err != nil {
return errors.Wrapf(err, "path element at %d is invalid", i)
}
}
return nil
}
func (vi *ValidatorImpl) verifyDeltaSet(deltaSet map[string]comparable, signedData []*cb.SignedData) error {
if len(deltaSet) == 0 {
return errors.Errorf("delta set was empty -- update would have no effect")
}
for key, value := range deltaSet {
logger.Debugf("Processing change to key: %s", key)
if err := validateModPolicy(value.modPolicy()); err != nil {
return errors.Wrapf(err, "invalid mod_policy for element %s", key)
}
existing, ok := vi.configMap[key]
if !ok {
if value.version() != 0 {
return errors.Errorf("attempted to set key %s to version %d, but key does not exist", key, value.version())
}
continue
}
if value.version() != existing.version()+1 {
return errors.Errorf("attempt to set key %s to version %d, but key is at version %d", key, value.version(), existing.version())
}
policy, ok := vi.policyForItem(existing)
if !ok {
return errors.Errorf("unexpected missing policy %s for item %s", existing.modPolicy(), key)
}
// Ensure the policy is satisfied
if err := policy.Evaluate(signedData); err != nil {
return errors.Wrapf(err, "policy for %s not satisfied", key)
}
}
return nil
}
func verifyFullProposedConfig(writeSet, fullProposedConfig map[string]comparable) error {
for key := range writeSet {
if _, ok := fullProposedConfig[key]; !ok {
return errors.Errorf("writeset contained key %s which did not appear in proposed config", key)
}
}
return nil
}
// authorizeUpdate validates that all modified config has the corresponding modification policies satisfied by the signature set
// it returns a map of the modified config
func (vi *ValidatorImpl) authorizeUpdate(configUpdateEnv *cb.ConfigUpdateEnvelope) (map[string]comparable, error) {
if configUpdateEnv == nil {
return nil, errors.Errorf("cannot process nil ConfigUpdateEnvelope")
}
configUpdate, err := UnmarshalConfigUpdate(configUpdateEnv.ConfigUpdate)
if err != nil {
return nil, err
}
if configUpdate.ChannelId != vi.channelID {
return nil, errors.Errorf("ConfigUpdate for channel '%s' but envelope for channel '%s'", configUpdate.ChannelId, vi.channelID)
}
readSet, err := mapConfig(configUpdate.ReadSet, vi.namespace)
if err != nil {
return nil, errors.Wrapf(err, "error mapping ReadSet")
}
err = vi.verifyReadSet(readSet)
if err != nil {
return nil, errors.Wrapf(err, "error validating ReadSet")
}
writeSet, err := mapConfig(configUpdate.WriteSet, vi.namespace)
if err != nil {
return nil, errors.Wrapf(err, "error mapping WriteSet")
}
deltaSet := computeDeltaSet(readSet, writeSet)
signedData, err := configUpdateEnv.AsSignedData()
if err != nil {
return nil, err
}
if err = vi.verifyDeltaSet(deltaSet, signedData); err != nil {
return nil, errors.Wrapf(err, "error validating DeltaSet")
}
fullProposedConfig := vi.computeUpdateResult(deltaSet)
if err := verifyFullProposedConfig(writeSet, fullProposedConfig); err != nil {
return nil, errors.Wrapf(err, "full config did not verify")
}
return fullProposedConfig, nil
}
func (vi *ValidatorImpl) policyForItem(item comparable) (policies.Policy, bool) {
manager := vi.pm
modPolicy := item.modPolicy()
logger.Debugf("Getting policy for item %s with mod_policy %s", item.key, modPolicy)
// If the mod_policy path is relative, get the right manager for the context
// If the item has a zero length path, it is the root group, use the base policy manager
// if the mod_policy path is absolute (starts with /) also use the base policy manager
if len(modPolicy) > 0 && modPolicy[0] != policies.PathSeparator[0] && len(item.path) != 0 {
var ok bool
manager, ok = manager.Manager(item.path[1:])
if !ok {
logger.Debugf("Could not find manager at path: %v", item.path[1:])
return nil, ok
}
// In the case of the group type, its key is part of its path for the purposes of finding the policy manager
if item.ConfigGroup != nil {
manager, ok = manager.Manager([]string{item.key})
}
if !ok {
logger.Debugf("Could not find group at subpath: %v", item.key)
return nil, ok
}
}
return manager.GetPolicy(item.modPolicy())
}
// computeUpdateResult takes a configMap generated by an update and produces a new configMap overlaying it onto the old config
func (vi *ValidatorImpl) computeUpdateResult(updatedConfig map[string]comparable) map[string]comparable {
newConfigMap := make(map[string]comparable)
for key, value := range vi.configMap {
newConfigMap[key] = value
}
for key, value := range updatedConfig {
newConfigMap[key] = value
}
return newConfigMap
}