-
Notifications
You must be signed in to change notification settings - Fork 8.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[FAB-9111] CC convenience layer for state-based EP
This implementation of the CC convenience layer interface facilitates the usage of state-based endorsement. It allows easy modification of a state-based endorsement policy providing Add/Delete functions that are based on MSP-IDs and automatically creates the underlying endorsement policy. Change-Id: Ic71f17ba6d3a6004e6d5b265bb8cb562eac2eddb Signed-off-by: Matthias Neugschwandtner <[email protected]> Signed-off-by: Alessandro Sorniotti <[email protected]>
- Loading branch information
Matthias Neugschwandtner
committed
Sep 5, 2018
1 parent
a8ea43f
commit 63c34a6
Showing
3 changed files
with
198 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
/* | ||
Copyright IBM Corp. All Rights Reserved. | ||
SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package statebased | ||
|
||
import ( | ||
"fmt" | ||
"sort" | ||
|
||
"github.com/golang/protobuf/proto" | ||
"github.com/hyperledger/fabric/common/cauthdsl" | ||
cb "github.com/hyperledger/fabric/protos/common" | ||
mb "github.com/hyperledger/fabric/protos/msp" | ||
"github.com/hyperledger/fabric/protos/utils" | ||
"github.com/pkg/errors" | ||
) | ||
|
||
// stateEP implements the KeyEndorsementPolicy | ||
type stateEP struct { | ||
orgs map[string]mb.MSPRole_MSPRoleType | ||
} | ||
|
||
// NewStateEP constructs a state-based endorsement policy from a given | ||
// serialized EP byte array. If the byte array is empty, a new EP is created. | ||
func NewStateEP(policy []byte) (KeyEndorsementPolicy, error) { | ||
s := &stateEP{orgs: make(map[string]mb.MSPRole_MSPRoleType)} | ||
if policy != nil { | ||
spe := &cb.SignaturePolicyEnvelope{} | ||
if err := proto.Unmarshal(policy, spe); err != nil { | ||
return nil, fmt.Errorf("Error unmarshaling to SignaturePolicy: %s", err) | ||
} | ||
|
||
err := s.setMSPIDsFromSP(spe) | ||
if err != nil { | ||
return nil, err | ||
} | ||
} | ||
return s, nil | ||
} | ||
|
||
// Policy returns the endorsement policy as bytes | ||
func (s *stateEP) Policy() ([]byte, error) { | ||
spe := s.policyFromMSPIDs() | ||
spBytes, err := proto.Marshal(spe) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return spBytes, nil | ||
} | ||
|
||
// AddOrgs adds the specified channel orgs to the existing key-level EP | ||
func (s *stateEP) AddOrgs(role RoleType, neworgs ...string) error { | ||
var mspRole mb.MSPRole_MSPRoleType | ||
switch role { | ||
case RoleTypeMember: | ||
mspRole = mb.MSPRole_MEMBER | ||
case RoleTypePeer: | ||
mspRole = mb.MSPRole_PEER | ||
default: | ||
return &RoleTypeDoesNotExistError{RoleType: role} | ||
} | ||
|
||
// add new orgs | ||
for _, addorg := range neworgs { | ||
s.orgs[addorg] = mspRole | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// DelOrgs delete the specified channel orgs from the existing key-level EP | ||
func (s *stateEP) DelOrgs(delorgs ...string) { | ||
for _, delorg := range delorgs { | ||
delete(s.orgs, delorg) | ||
} | ||
} | ||
|
||
// ListOrgs returns an array of channel orgs that are required to endorse chnages | ||
func (s *stateEP) ListOrgs() []string { | ||
orgNames := make([]string, 0, len(s.orgs)) | ||
for mspid := range s.orgs { | ||
orgNames = append(orgNames, mspid) | ||
} | ||
return orgNames | ||
} | ||
|
||
func (s *stateEP) setMSPIDsFromSP(sp *cb.SignaturePolicyEnvelope) error { | ||
// iterate over the identities in this envelope | ||
for _, identity := range sp.Identities { | ||
// this imlementation only supports the ROLE type | ||
if identity.PrincipalClassification == mb.MSPPrincipal_ROLE { | ||
msprole := &mb.MSPRole{} | ||
err := proto.Unmarshal(identity.Principal, msprole) | ||
if err != nil { | ||
return errors.Wrapf(err, "error unmarshaling msp principal") | ||
} | ||
s.orgs[msprole.GetMspIdentifier()] = msprole.GetRole() | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func (s *stateEP) policyFromMSPIDs() *cb.SignaturePolicyEnvelope { | ||
mspids := s.ListOrgs() | ||
sort.Strings(mspids) | ||
principals := make([]*mb.MSPPrincipal, len(mspids)) | ||
sigspolicy := make([]*cb.SignaturePolicy, len(mspids)) | ||
for i, id := range mspids { | ||
principals[i] = &mb.MSPPrincipal{ | ||
PrincipalClassification: mb.MSPPrincipal_ROLE, | ||
Principal: utils.MarshalOrPanic(&mb.MSPRole{Role: s.orgs[id], MspIdentifier: id}), | ||
} | ||
sigspolicy[i] = cauthdsl.SignedBy(int32(i)) | ||
} | ||
|
||
// create the policy: it requires exactly 1 signature from all of the principals | ||
p := &cb.SignaturePolicyEnvelope{ | ||
Version: 0, | ||
Rule: cauthdsl.NOutOf(int32(len(mspids)), sigspolicy), | ||
Identities: principals, | ||
} | ||
return p | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
/* | ||
Copyright IBM Corp. All Rights Reserved. | ||
SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package statebased_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/hyperledger/fabric/common/cauthdsl" | ||
|
||
"github.com/golang/protobuf/proto" | ||
"github.com/hyperledger/fabric/core/chaincode/shim/ext/statebased" | ||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestAddOrg(t *testing.T) { | ||
// add an org | ||
ep, err := statebased.NewStateEP(nil) | ||
assert.NoError(t, err) | ||
err = ep.AddOrgs(statebased.RoleTypePeer, "Org1") | ||
assert.NoError(t, err) | ||
err = ep.AddOrgs("unknown", "Org1") | ||
assert.Error(t, err) | ||
rtdnee, ok := err.(*statebased.RoleTypeDoesNotExistError) | ||
assert.True(t, ok) | ||
assert.Equal(t, statebased.RoleType("unknown"), rtdnee.RoleType) | ||
epBytes, err := ep.Policy() | ||
assert.NoError(t, err) | ||
expectedEP := cauthdsl.SignedByMspPeer("Org1") | ||
expectedEPBytes, err := proto.Marshal(expectedEP) | ||
assert.NoError(t, err) | ||
assert.Equal(t, expectedEPBytes, epBytes) | ||
} | ||
|
||
func TestListOrgs(t *testing.T) { | ||
expectedEP := cauthdsl.SignedByMspPeer("Org1") | ||
expectedEPBytes, err := proto.Marshal(expectedEP) | ||
assert.NoError(t, err) | ||
|
||
// retrieve the orgs | ||
ep, err := statebased.NewStateEP(expectedEPBytes) | ||
orgs := ep.ListOrgs() | ||
assert.Equal(t, []string{"Org1"}, orgs) | ||
} | ||
|
||
func TestDelAddOrg(t *testing.T) { | ||
expectedEP := cauthdsl.SignedByMspPeer("Org1") | ||
expectedEPBytes, err := proto.Marshal(expectedEP) | ||
assert.NoError(t, err) | ||
ep, err := statebased.NewStateEP(expectedEPBytes) | ||
|
||
// retrieve the orgs | ||
orgs := ep.ListOrgs() | ||
assert.Equal(t, []string{"Org1"}, orgs) | ||
|
||
// mod the endorsement policy | ||
ep.AddOrgs(statebased.RoleTypePeer, "Org2") | ||
ep.DelOrgs("Org1") | ||
|
||
// check whether what is stored is correct | ||
epBytes, err := ep.Policy() | ||
assert.NoError(t, err) | ||
expectedEP = cauthdsl.SignedByMspPeer("Org2") | ||
expectedEPBytes, err = proto.Marshal(expectedEP) | ||
assert.NoError(t, err) | ||
assert.Equal(t, expectedEPBytes, epBytes) | ||
} |