-
Notifications
You must be signed in to change notification settings - Fork 8.8k
/
update.go
179 lines (149 loc) · 5.52 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
/*
Copyright IBM Corp. 2016-2017 All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package configtx
import (
"fmt"
"github.com/hyperledger/fabric/common/policies"
cb "github.com/hyperledger/fabric/protos/common"
"github.com/hyperledger/fabric/protos/utils"
)
func (c *configSet) verifyReadSet(readSet map[string]comparable) error {
for key, value := range readSet {
existing, ok := c.configMap[key]
if !ok {
return fmt.Errorf("Existing config does not contain element for %s but was in the read set", key)
}
if existing.version() != value.version() {
return fmt.Errorf("Readset expected key %s at version %d, but got 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 current config
result[key] = value
}
return result
}
func (cm *configManager) verifyDeltaSet(deltaSet map[string]comparable, signedData []*cb.SignedData) error {
for key, value := range deltaSet {
existing, ok := cm.current.configMap[key]
if !ok {
if value.version() != 0 {
return fmt.Errorf("Attempted to set key %s to version %d, but key does not exist", key, value.version())
} else {
continue
}
}
if value.version() != existing.version()+1 {
return fmt.Errorf("Attempt to set key %s to version %d, but key is at version %d", key, value.version(), existing.version())
}
policy, ok := cm.policyForItem(existing)
if !ok {
return fmt.Errorf("Unexpected missing policy %s for item %s", existing.modPolicy(), key)
}
// Ensure the policy is satisfied
if err := policy.Evaluate(signedData); err != nil {
return fmt.Errorf("Policy for %s not satisfied: %s", key, err)
}
}
return nil
}
func verifyFullProposedConfig(writeSet, fullProposedConfig map[string]comparable) error {
for key, _ := range writeSet {
if _, ok := fullProposedConfig[key]; !ok {
return fmt.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 (cm *configManager) authorizeUpdate(configUpdateEnv *cb.ConfigUpdateEnvelope) (map[string]comparable, error) {
if configUpdateEnv == nil {
return nil, fmt.Errorf("Cannot process nil ConfigUpdateEnvelope")
}
configUpdate, err := UnmarshalConfigUpdate(configUpdateEnv.ConfigUpdate)
if err != nil {
return nil, err
}
if configUpdate.ChannelId != cm.current.channelID {
return nil, fmt.Errorf("Update not for correct channel: %s for %s", configUpdate.ChannelId, cm.current.channelID)
}
readSet, err := mapConfig(configUpdate.ReadSet)
if err != nil {
return nil, fmt.Errorf("Error mapping ReadSet: %s", err)
}
err = cm.current.verifyReadSet(readSet)
if err != nil {
return nil, fmt.Errorf("Error validating ReadSet: %s", err)
}
writeSet, err := mapConfig(configUpdate.WriteSet)
if err != nil {
return nil, fmt.Errorf("Error mapping WriteSet: %s", err)
}
deltaSet := computeDeltaSet(readSet, writeSet)
signedData, err := configUpdateEnv.AsSignedData()
if err != nil {
return nil, err
}
if err = cm.verifyDeltaSet(deltaSet, signedData); err != nil {
return nil, fmt.Errorf("Error validating DeltaSet: %s", err)
}
fullProposedConfig := cm.computeUpdateResult(deltaSet)
if err := verifyFullProposedConfig(writeSet, fullProposedConfig); err != nil {
return nil, fmt.Errorf("Full config did not verify: %s", err)
}
return fullProposedConfig, nil
}
func (cm *configManager) policyForItem(item comparable) (policies.Policy, bool) {
// path is always at least of length 1
manager, ok := cm.PolicyManager().Manager(item.path[1:])
if !ok {
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 {
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 (cm *configManager) computeUpdateResult(updatedConfig map[string]comparable) map[string]comparable {
newConfigMap := make(map[string]comparable)
for key, value := range cm.current.configMap {
newConfigMap[key] = value
}
for key, value := range updatedConfig {
newConfigMap[key] = value
}
return newConfigMap
}
func envelopeToConfigUpdate(configtx *cb.Envelope) (*cb.ConfigUpdateEnvelope, error) {
configUpdateEnv := &cb.ConfigUpdateEnvelope{}
_, err := utils.UnmarshalEnvelopeOfType(configtx, cb.HeaderType_CONFIG_UPDATE, configUpdateEnv)
if err != nil {
return nil, err
}
return configUpdateEnv, nil
}