Skip to content

Commit

Permalink
[FAB-2323] Implement ImplicitMetaPolicy
Browse files Browse the repository at this point in the history
https://jira.hyperledger.org/browse/FAB-2323

This CR adds support to the policies manager for evaluating the new
ImplicitMetaPolicy policy type.

Unlike the cauthdsl policy provider, the implicit metapolicy needs to
live inside the policy manager to hook into the transactional nature of
the policy manager.

Change-Id: I8f11ea46950da6566833daa65b2e056faa2f63d6
Signed-off-by: Jason Yellick <jyellick@us.ibm.com>
  • Loading branch information
Jason Yellick committed Feb 17, 2017
1 parent 1ffb87e commit 018d888
Show file tree
Hide file tree
Showing 4 changed files with 215 additions and 32 deletions.
13 changes: 1 addition & 12 deletions common/mocks/policies/policies.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,21 +44,10 @@ type Manager struct {
// PolicyMap is returned is used to look up Policies in
PolicyMap map[string]*Policy

// SubManagers is used for the return value of Manager and SubManagers
// SubManagers is used for the return value of Manager
SubManagersMap map[string]*Manager
}

// Managers returns the values of SubManagers
func (m *Manager) SubManagers() []policies.Manager {
result := make([]policies.Manager, len(m.SubManagersMap))
i := 0
for _, manager := range m.SubManagersMap {
result[i] = manager
i++
}
return result
}

// PolicyNames panics
func (m *Manager) PolicyNames() []string {
panic("Unimplimented")
Expand Down
83 changes: 83 additions & 0 deletions common/policies/implicitmeta.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
Copyright IBM Corp. 2016 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 policies

import (
"fmt"

cb "github.com/hyperledger/fabric/protos/common"

"github.com/golang/protobuf/proto"
)

type implicitMetaPolicy struct {
conf *cb.ImplicitMetaPolicy
threshold int
subPolicies []Policy
}

// NewPolicy creates a new policy based on the policy bytes
func newImplicitMetaPolicy(data []byte) (*implicitMetaPolicy, error) {
imp := &cb.ImplicitMetaPolicy{}
if err := proto.Unmarshal(data, imp); err != nil {
return nil, fmt.Errorf("Error unmarshaling to ImplicitMetaPolicy: %s", err)
}

return &implicitMetaPolicy{
conf: imp,
}, nil
}

func (imp *implicitMetaPolicy) initialize(config *policyConfig) {
imp.subPolicies = make([]Policy, len(config.managers))
i := 0
for _, manager := range config.managers {
imp.subPolicies[i], _ = manager.GetPolicy(imp.conf.SubPolicy)
i++
}

switch imp.conf.Rule {
case cb.ImplicitMetaPolicy_ANY:
imp.threshold = 1
case cb.ImplicitMetaPolicy_ALL:
imp.threshold = len(imp.subPolicies)
case cb.ImplicitMetaPolicy_MAJORITY:
imp.threshold = len(imp.subPolicies)/2 + 1
}

// In the special case that there are no policies, consider 0 to be a majority or any
if len(imp.subPolicies) == 0 {
imp.threshold = 0
}
}

// Evaluate takes a set of SignedData and evaluates whether this set of signatures satisfies the policy
func (imp *implicitMetaPolicy) Evaluate(signatureSet []*cb.SignedData) error {
remaining := imp.threshold
for _, policy := range imp.subPolicies {
if policy.Evaluate(signatureSet) == nil {
remaining--
if remaining == 0 {
return nil
}
}
}
if remaining == 0 {
return nil
}
return fmt.Errorf("Failed to reach implicit threshold of %d sub-policies, required %d remaining", imp.threshold, remaining)
}
101 changes: 101 additions & 0 deletions common/policies/implicitmeta_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
Copyright IBM Corp. 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 policies

import (
"fmt"
"testing"

cb "github.com/hyperledger/fabric/protos/common"
"github.com/hyperledger/fabric/protos/utils"

"github.com/stretchr/testify/assert"
)

const TestPolicyName = "TestPolicyName"

type acceptPolicy struct{}

func (rp acceptPolicy) Evaluate(signedData []*cb.SignedData) error {
return nil
}

func TestimplicitMarshalError(t *testing.T) {
_, err := newImplicitMetaPolicy([]byte("GARBAGE"))
assert.Error(t, err, "Should have errored unmarshaling garbage")
}

func makeManagers(count, passing int) map[string]*ManagerImpl {
result := make(map[string]*ManagerImpl)
remaining := passing
for i := 0; i < count; i++ {
policyMap := make(map[string]Policy)
if remaining > 0 {
policyMap[TestPolicyName] = acceptPolicy{}
}
remaining--

result[fmt.Sprintf("%d", i)] = &ManagerImpl{
config: &policyConfig{
policies: policyMap,
},
}
}
return result
}

// makePolicyTest creates an implicitMetaPolicy with a set of
func runPolicyTest(rule cb.ImplicitMetaPolicy_Rule, managerCount int, passingCount int) error {
imp, err := newImplicitMetaPolicy(utils.MarshalOrPanic(&cb.ImplicitMetaPolicy{
Rule: rule,
SubPolicy: TestPolicyName,
}))
if err != nil {
panic(err)
}

imp.initialize(&policyConfig{
managers: makeManagers(managerCount, passingCount),
})

return imp.Evaluate(nil)
}

func TestImplicitMetaAny(t *testing.T) {
assert.NoError(t, runPolicyTest(cb.ImplicitMetaPolicy_ANY, 1, 1))
assert.NoError(t, runPolicyTest(cb.ImplicitMetaPolicy_ANY, 10, 1))
assert.NoError(t, runPolicyTest(cb.ImplicitMetaPolicy_ANY, 10, 8))
assert.Error(t, runPolicyTest(cb.ImplicitMetaPolicy_ANY, 10, 0))
assert.NoError(t, runPolicyTest(cb.ImplicitMetaPolicy_ANY, 0, 0))
}

func TestImplicitMetaAll(t *testing.T) {
assert.NoError(t, runPolicyTest(cb.ImplicitMetaPolicy_ALL, 1, 1))
assert.Error(t, runPolicyTest(cb.ImplicitMetaPolicy_ALL, 10, 1))
assert.NoError(t, runPolicyTest(cb.ImplicitMetaPolicy_ALL, 10, 10))
assert.Error(t, runPolicyTest(cb.ImplicitMetaPolicy_ALL, 10, 0))
assert.NoError(t, runPolicyTest(cb.ImplicitMetaPolicy_ALL, 0, 0))
}

func TestImplicitMetaMajority(t *testing.T) {
assert.NoError(t, runPolicyTest(cb.ImplicitMetaPolicy_MAJORITY, 1, 1))
assert.Error(t, runPolicyTest(cb.ImplicitMetaPolicy_MAJORITY, 10, 5))
assert.NoError(t, runPolicyTest(cb.ImplicitMetaPolicy_MAJORITY, 10, 6))
assert.NoError(t, runPolicyTest(cb.ImplicitMetaPolicy_MAJORITY, 3, 2))
assert.Error(t, runPolicyTest(cb.ImplicitMetaPolicy_MAJORITY, 10, 0))
assert.NoError(t, runPolicyTest(cb.ImplicitMetaPolicy_MAJORITY, 0, 0))
}
50 changes: 30 additions & 20 deletions common/policies/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,6 @@ type Manager interface {

// Policies returns all policy names defined in the manager
PolicyNames() []string

// SubManagers() returns all managers immediately descended from this one
SubManagers() []Manager
}

// Proposer is the interface used by the configtx manager for policy management
Expand All @@ -70,6 +67,7 @@ type Provider interface {
type policyConfig struct {
policies map[string]Policy
managers map[string]*ManagerImpl
imps []*implicitMetaPolicy
}

// ManagerImpl is an implementation of Manager and configtx.ConfigHandler
Expand All @@ -83,6 +81,11 @@ type ManagerImpl struct {

// NewManagerImpl creates a new ManagerImpl with the given CryptoHelper
func NewManagerImpl(basePath string, providers map[int32]Provider) *ManagerImpl {
_, ok := providers[int32(cb.Policy_IMPLICIT_META)]
if ok {
logger.Panicf("ImplicitMetaPolicy type must be provider by the policy manager")
}

return &ManagerImpl{
basePath: basePath,
providers: providers,
Expand All @@ -104,16 +107,6 @@ func (pm *ManagerImpl) BasePath() string {
return pm.basePath
}

func (pm *ManagerImpl) SubManagers() []Manager {
managers := make([]Manager, len(pm.config.managers))
i := 0
for _, manager := range pm.config.managers {
managers[i] = manager
i++
}
return managers
}

func (pm *ManagerImpl) PolicyNames() []string {
policyNames := make([]string, len(pm.config.policies))
i := 0
Expand Down Expand Up @@ -192,6 +185,11 @@ func (pm *ManagerImpl) CommitProposals() {
}
}

// Now that all the policies are present, initialize the meta policies
for _, imp := range pm.pendingConfig.imps {
imp.initialize(pm.pendingConfig)
}

pm.config = pm.pendingConfig
pm.pendingConfig = nil
}
Expand All @@ -203,14 +201,26 @@ func (pm *ManagerImpl) ProposePolicy(key string, configPolicy *cb.ConfigPolicy)
return fmt.Errorf("Policy cannot be nil")
}

provider, ok := pm.providers[int32(policy.Type)]
if !ok {
return fmt.Errorf("Unknown policy type: %v", policy.Type)
}
var cPolicy Policy

cPolicy, err := provider.NewPolicy(policy.Policy)
if err != nil {
return err
if policy.Type == int32(cb.Policy_IMPLICIT_META) {
imp, err := newImplicitMetaPolicy(policy.Policy)
if err != nil {
return err
}
pm.pendingConfig.imps = append(pm.pendingConfig.imps, imp)
cPolicy = imp
} else {
provider, ok := pm.providers[int32(policy.Type)]
if !ok {
return fmt.Errorf("Unknown policy type: %v", policy.Type)
}

var err error
cPolicy, err = provider.NewPolicy(policy.Policy)
if err != nil {
return err
}
}

pm.pendingConfig.policies[key] = cPolicy
Expand Down

0 comments on commit 018d888

Please sign in to comment.