235 lines
6.9 KiB
Go
235 lines
6.9 KiB
Go
/*
|
|
Copyright IBM Corp. 2017 All Rights Reserved.
|
|
|
|
SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
package update
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
|
|
"github.com/golang/protobuf/proto"
|
|
cb "github.com/hyperledger/fabric-protos-go/common"
|
|
"github.com/hyperledger/fabric/protoutil"
|
|
)
|
|
|
|
func computePoliciesMapUpdate(original, updated map[string]*cb.ConfigPolicy) (readSet, writeSet, sameSet map[string]*cb.ConfigPolicy, updatedMembers bool) {
|
|
readSet = make(map[string]*cb.ConfigPolicy)
|
|
writeSet = make(map[string]*cb.ConfigPolicy)
|
|
|
|
// All modified config goes into the read/write sets, but in case the map membership changes, we retain the
|
|
// config which was the same to add to the read/write sets
|
|
sameSet = make(map[string]*cb.ConfigPolicy)
|
|
|
|
for policyName, originalPolicy := range original {
|
|
updatedPolicy, ok := updated[policyName]
|
|
if !ok {
|
|
updatedMembers = true
|
|
continue
|
|
}
|
|
|
|
if originalPolicy.ModPolicy == updatedPolicy.ModPolicy && proto.Equal(originalPolicy.Policy, updatedPolicy.Policy) {
|
|
sameSet[policyName] = &cb.ConfigPolicy{
|
|
Version: originalPolicy.Version,
|
|
}
|
|
continue
|
|
}
|
|
|
|
writeSet[policyName] = &cb.ConfigPolicy{
|
|
Version: originalPolicy.Version + 1,
|
|
ModPolicy: updatedPolicy.ModPolicy,
|
|
Policy: updatedPolicy.Policy,
|
|
}
|
|
}
|
|
|
|
for policyName, updatedPolicy := range updated {
|
|
if _, ok := original[policyName]; ok {
|
|
// If the updatedPolicy is in the original set of policies, it was already handled
|
|
continue
|
|
}
|
|
updatedMembers = true
|
|
writeSet[policyName] = &cb.ConfigPolicy{
|
|
Version: 0,
|
|
ModPolicy: updatedPolicy.ModPolicy,
|
|
Policy: updatedPolicy.Policy,
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func computeValuesMapUpdate(original, updated map[string]*cb.ConfigValue) (readSet, writeSet, sameSet map[string]*cb.ConfigValue, updatedMembers bool) {
|
|
readSet = make(map[string]*cb.ConfigValue)
|
|
writeSet = make(map[string]*cb.ConfigValue)
|
|
|
|
// All modified config goes into the read/write sets, but in case the map membership changes, we retain the
|
|
// config which was the same to add to the read/write sets
|
|
sameSet = make(map[string]*cb.ConfigValue)
|
|
|
|
for valueName, originalValue := range original {
|
|
updatedValue, ok := updated[valueName]
|
|
if !ok {
|
|
updatedMembers = true
|
|
continue
|
|
}
|
|
|
|
if originalValue.ModPolicy == updatedValue.ModPolicy && bytes.Equal(originalValue.Value, updatedValue.Value) {
|
|
sameSet[valueName] = &cb.ConfigValue{
|
|
Version: originalValue.Version,
|
|
}
|
|
continue
|
|
}
|
|
|
|
writeSet[valueName] = &cb.ConfigValue{
|
|
Version: originalValue.Version + 1,
|
|
ModPolicy: updatedValue.ModPolicy,
|
|
Value: updatedValue.Value,
|
|
}
|
|
}
|
|
|
|
for valueName, updatedValue := range updated {
|
|
if _, ok := original[valueName]; ok {
|
|
// If the updatedValue is in the original set of values, it was already handled
|
|
continue
|
|
}
|
|
updatedMembers = true
|
|
writeSet[valueName] = &cb.ConfigValue{
|
|
Version: 0,
|
|
ModPolicy: updatedValue.ModPolicy,
|
|
Value: updatedValue.Value,
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func computeGroupsMapUpdate(original, updated map[string]*cb.ConfigGroup) (readSet, writeSet, sameSet map[string]*cb.ConfigGroup, updatedMembers bool) {
|
|
readSet = make(map[string]*cb.ConfigGroup)
|
|
writeSet = make(map[string]*cb.ConfigGroup)
|
|
|
|
// All modified config goes into the read/write sets, but in case the map membership changes, we retain the
|
|
// config which was the same to add to the read/write sets
|
|
sameSet = make(map[string]*cb.ConfigGroup)
|
|
|
|
for groupName, originalGroup := range original {
|
|
updatedGroup, ok := updated[groupName]
|
|
if !ok {
|
|
updatedMembers = true
|
|
continue
|
|
}
|
|
|
|
groupReadSet, groupWriteSet, groupUpdated := computeGroupUpdate(originalGroup, updatedGroup)
|
|
if !groupUpdated {
|
|
sameSet[groupName] = groupReadSet
|
|
continue
|
|
}
|
|
|
|
readSet[groupName] = groupReadSet
|
|
writeSet[groupName] = groupWriteSet
|
|
|
|
}
|
|
|
|
for groupName, updatedGroup := range updated {
|
|
if _, ok := original[groupName]; ok {
|
|
// If the updatedGroup is in the original set of groups, it was already handled
|
|
continue
|
|
}
|
|
updatedMembers = true
|
|
_, groupWriteSet, _ := computeGroupUpdate(protoutil.NewConfigGroup(), updatedGroup)
|
|
writeSet[groupName] = &cb.ConfigGroup{
|
|
Version: 0,
|
|
ModPolicy: updatedGroup.ModPolicy,
|
|
Policies: groupWriteSet.Policies,
|
|
Values: groupWriteSet.Values,
|
|
Groups: groupWriteSet.Groups,
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func computeGroupUpdate(original, updated *cb.ConfigGroup) (readSet, writeSet *cb.ConfigGroup, updatedGroup bool) {
|
|
readSetPolicies, writeSetPolicies, sameSetPolicies, policiesMembersUpdated := computePoliciesMapUpdate(original.Policies, updated.Policies)
|
|
readSetValues, writeSetValues, sameSetValues, valuesMembersUpdated := computeValuesMapUpdate(original.Values, updated.Values)
|
|
readSetGroups, writeSetGroups, sameSetGroups, groupsMembersUpdated := computeGroupsMapUpdate(original.Groups, updated.Groups)
|
|
|
|
// If the updated group is 'Equal' to the updated group (none of the members nor the mod policy changed)
|
|
if !(policiesMembersUpdated || valuesMembersUpdated || groupsMembersUpdated || original.ModPolicy != updated.ModPolicy) {
|
|
|
|
// If there were no modified entries in any of the policies/values/groups maps
|
|
if len(readSetPolicies) == 0 &&
|
|
len(writeSetPolicies) == 0 &&
|
|
len(readSetValues) == 0 &&
|
|
len(writeSetValues) == 0 &&
|
|
len(readSetGroups) == 0 &&
|
|
len(writeSetGroups) == 0 {
|
|
return &cb.ConfigGroup{
|
|
Version: original.Version,
|
|
}, &cb.ConfigGroup{
|
|
Version: original.Version,
|
|
}, false
|
|
}
|
|
|
|
return &cb.ConfigGroup{
|
|
Version: original.Version,
|
|
Policies: readSetPolicies,
|
|
Values: readSetValues,
|
|
Groups: readSetGroups,
|
|
}, &cb.ConfigGroup{
|
|
Version: original.Version,
|
|
Policies: writeSetPolicies,
|
|
Values: writeSetValues,
|
|
Groups: writeSetGroups,
|
|
}, true
|
|
}
|
|
|
|
for k, samePolicy := range sameSetPolicies {
|
|
readSetPolicies[k] = samePolicy
|
|
writeSetPolicies[k] = samePolicy
|
|
}
|
|
|
|
for k, sameValue := range sameSetValues {
|
|
readSetValues[k] = sameValue
|
|
writeSetValues[k] = sameValue
|
|
}
|
|
|
|
for k, sameGroup := range sameSetGroups {
|
|
readSetGroups[k] = sameGroup
|
|
writeSetGroups[k] = sameGroup
|
|
}
|
|
|
|
return &cb.ConfigGroup{
|
|
Version: original.Version,
|
|
Policies: readSetPolicies,
|
|
Values: readSetValues,
|
|
Groups: readSetGroups,
|
|
}, &cb.ConfigGroup{
|
|
Version: original.Version + 1,
|
|
Policies: writeSetPolicies,
|
|
Values: writeSetValues,
|
|
Groups: writeSetGroups,
|
|
ModPolicy: updated.ModPolicy,
|
|
}, true
|
|
}
|
|
|
|
func Compute(original, updated *cb.Config) (*cb.ConfigUpdate, error) {
|
|
if original.ChannelGroup == nil {
|
|
return nil, fmt.Errorf("no channel group included for original config")
|
|
}
|
|
|
|
if updated.ChannelGroup == nil {
|
|
return nil, fmt.Errorf("no channel group included for updated config")
|
|
}
|
|
|
|
readSet, writeSet, groupUpdated := computeGroupUpdate(original.ChannelGroup, updated.ChannelGroup)
|
|
if !groupUpdated {
|
|
return nil, fmt.Errorf("no differences detected between original and updated config")
|
|
}
|
|
return &cb.ConfigUpdate{
|
|
ReadSet: readSet,
|
|
WriteSet: writeSet,
|
|
}, nil
|
|
}
|