Skip to content

Commit

Permalink
[FAB-6664] Add chaincodes group parsing
Browse files Browse the repository at this point in the history
The previous CR in this series added the proto definitions for the new
chaincode lifecycle config structures.  This CR actually implements the
parsing of these structures in the resources config.

It defines new interfaces which may be leveraged by the
endorsing/validating pieces of the chaincode.

Change-Id: Ic2668594a07f53f24f7bcc285f8fb74864772f6c
Signed-off-by: Jason Yellick <jyellick@us.ibm.com>
  • Loading branch information
Jason Yellick committed Nov 2, 2017
1 parent c3bfd6d commit ecabe49
Show file tree
Hide file tree
Showing 11 changed files with 525 additions and 68 deletions.
67 changes: 67 additions & 0 deletions common/resourcesconfig/apis_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
Copyright IBM Corp. 2017 All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package resourcesconfig

import (
"testing"

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

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

const (
sampleAPI1Name = "Foo"
sampleAPI1PolicyRef = "foo"

sampleAPI2Name = "Bar"
sampleAPI2PolicyRef = "/Channel/foo"
)

var sampleAPIsGroup = &cb.ConfigGroup{
Values: map[string]*cb.ConfigValue{
sampleAPI1Name: &cb.ConfigValue{
Value: utils.MarshalOrPanic(&pb.APIResource{
PolicyRef: sampleAPI1PolicyRef,
}),
},
sampleAPI2Name: &cb.ConfigValue{
Value: utils.MarshalOrPanic(&pb.APIResource{
PolicyRef: sampleAPI2PolicyRef,
}),
},
},
}

func TestGreenAPIsPath(t *testing.T) {
ag, err := newAPIsGroup(sampleAPIsGroup)
assert.NotNil(t, ag)
assert.NoError(t, err)

t.Run("PresentAPIs", func(t *testing.T) {
assert.Equal(t, "/Resources/APIs/"+sampleAPI1PolicyRef, ag.PolicyRefForAPI(sampleAPI1Name))
assert.Equal(t, sampleAPI2PolicyRef, ag.PolicyRefForAPI(sampleAPI2Name))
})

t.Run("MissingAPIs", func(t *testing.T) {
assert.Empty(t, ag.PolicyRefForAPI("missing"))
})
}

func TestBadSubgroupsAPIsGroup(t *testing.T) {
ccg, err := newAPIsGroup(&cb.ConfigGroup{
Groups: map[string]*cb.ConfigGroup{
"subGroup": &cb.ConfigGroup{},
},
})

assert.Nil(t, ccg)
assert.Error(t, err)
assert.Regexp(t, "apis group does not support sub-groups", err.Error())
}
18 changes: 11 additions & 7 deletions common/resourcesconfig/bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,12 @@ import (
"github.com/hyperledger/fabric/protos/utils"
)

// RootGroupKey is the namespace in the config tree for this set of config
const RootGroupKey = "Resources"

var logger = flogging.MustGetLogger("common/config/resource")

// PolicyMapper is an interface for
type PolicyMapper interface {
// PolicyRefForAPI takes the name of an API, and returns the policy name
// or the empty string if the API is not found
PolicyRefForAPI(apiName string) string
}

// Bundle stores an immutable group of resources configuration
type Bundle struct {
rg *resourceGroup
cm configtxapi.Manager
Expand Down Expand Up @@ -95,18 +90,27 @@ func New(envConfig *cb.Envelope, mspManager msp.MSPManager, channelPolicyManager
return b, nil
}

// RootGroupKey returns the name of the key for the root group (the namespace for this config).
func (b *Bundle) RootGroupKey() string {
return RootGroupKey
}

// ConfigtxManager returns a reference to a configtx.Manager which can process updates to this config.
func (b *Bundle) ConfigtxManager() configtxapi.Manager {
return b.cm
}

// PolicyManager returns a policy manager which can resolve names both in the /Channel and /Resources namespaces.
func (b *Bundle) PolicyManager() policies.Manager {
return b.pm
}

// APIPolicyMapper returns a way to map API names to policies governing their invocation.
func (b *Bundle) APIPolicyMapper() PolicyMapper {
return b.rg.apisGroup
}

// ChaincodeRegistery returns a way to query for chaincodes defined in this channel.
func (b *Bundle) ChaincodeRegistry() ChaincodeRegistry {
return b.rg.chaincodesGroup
}
74 changes: 13 additions & 61 deletions common/resourcesconfig/bundle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ package resourcesconfig
import (
"testing"

"github.com/hyperledger/fabric/common/cauthdsl"
cb "github.com/hyperledger/fabric/protos/common"
pb "github.com/hyperledger/fabric/protos/peer"
"github.com/hyperledger/fabric/protos/utils"

logging "github.com/op/go-logging"
Expand All @@ -22,88 +20,42 @@ func init() {
logging.SetLevel(logging.DEBUG, "")
}

var dummyPolicy = &cb.ConfigPolicy{
Policy: &cb.Policy{
Type: int32(cb.Policy_SIGNATURE),
Value: utils.MarshalOrPanic(cauthdsl.AcceptAllPolicy),
},
func TestBundleInterface(t *testing.T) {
_ = Resources(&Bundle{})
}

func TestBundleGreenPath(t *testing.T) {
func TestBundle(t *testing.T) {
env, err := utils.CreateSignedEnvelope(cb.HeaderType_CONFIG, "foo", nil, &cb.ConfigEnvelope{
Config: &cb.Config{
ChannelGroup: &cb.ConfigGroup{
Groups: map[string]*cb.ConfigGroup{
APIsGroupKey: &cb.ConfigGroup{
Values: map[string]*cb.ConfigValue{
"Foo": &cb.ConfigValue{
Value: utils.MarshalOrPanic(&pb.APIResource{
PolicyRef: "foo",
}),
},
"Bar": &cb.ConfigValue{
Value: utils.MarshalOrPanic(&pb.APIResource{
PolicyRef: "/Channel/foo",
}),
},
},
},
PeerPoliciesGroupKey: &cb.ConfigGroup{
Policies: map[string]*cb.ConfigPolicy{
"foo": dummyPolicy,
"bar": dummyPolicy,
},
Groups: map[string]*cb.ConfigGroup{
"subGroup": &cb.ConfigGroup{
Policies: map[string]*cb.ConfigPolicy{
"other": dummyPolicy,
},
},
},
},
},
},
ChannelGroup: sampleResourceGroup,
},
}, 0, 0)
assert.NoError(t, err)

b, err := New(env, nil, nil)
assert.NoError(t, err)
assert.NotNil(t, b)
assert.Equal(t, "/Resources/APIs/foo", b.APIPolicyMapper().PolicyRefForAPI("Foo"))
assert.Equal(t, "/Channel/foo", b.APIPolicyMapper().PolicyRefForAPI("Bar"))

t.Run("Code coverage nits", func(t *testing.T) {
assert.Equal(t, b.RootGroupKey(), RootGroupKey)
assert.NotNil(t, b.ConfigtxManager())
assert.NotNil(t, b.PolicyManager())
})
assert.Equal(t, b.RootGroupKey(), RootGroupKey)
assert.NotNil(t, b.ConfigtxManager())
assert.NotNil(t, b.PolicyManager())
assert.NotNil(t, b.APIPolicyMapper())
assert.NotNil(t, b.ChaincodeRegistry())
}

func TestBundleBadSubGroup(t *testing.T) {
func TestBundleFailure(t *testing.T) {
env, err := utils.CreateSignedEnvelope(cb.HeaderType_CONFIG, "foo", nil, &cb.ConfigEnvelope{
Config: &cb.Config{
ChannelGroup: &cb.ConfigGroup{
Groups: map[string]*cb.ConfigGroup{
PeerPoliciesGroupKey: &cb.ConfigGroup{
Values: map[string]*cb.ConfigValue{
"Foo": &cb.ConfigValue{
Value: utils.MarshalOrPanic(&pb.APIResource{
PolicyRef: "foo",
}),
},
},
Policies: map[string]*cb.ConfigPolicy{
"other": dummyPolicy,
},
},
"badsubgroup": &cb.ConfigGroup{},
},
},
},
}, 0, 0)
assert.NoError(t, err)

_, err = New(env, nil, nil)
b, err := New(env, nil, nil)
assert.Error(t, err)
assert.Regexp(t, "sub-groups not allowed to have values", err.Error())
assert.Nil(t, b)
}
72 changes: 72 additions & 0 deletions common/resourcesconfig/chaincode.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
Copyright IBM Corp. 2017 All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package resourcesconfig

import (
"github.com/hyperledger/fabric/common/channelconfig"
cb "github.com/hyperledger/fabric/protos/common"
pb "github.com/hyperledger/fabric/protos/peer"

"github.com/pkg/errors"
)

type chaincodeProtos struct {
ChaincodeIdentifier *pb.ChaincodeIdentifier
ChaincodeValidation *pb.ChaincodeValidation
ChaincodeEndorsement *pb.ChaincodeEndorsement
}

// ChaincodeGroup represents the ConfigGroup named Chaincodes off the resources group
type ChaincodeGroup struct {
name string
protos *chaincodeProtos
}

// Name returns the name of this chaincode (the name it was put in the ChaincodeRegistry with).
func (cg *ChaincodeGroup) Name() string {
return cg.name
}

// Hash returns the hash of the chaincode.
func (cg *ChaincodeGroup) Hash() []byte {
return cg.protos.ChaincodeIdentifier.Hash
}

// Version returns the version of the chaincode.
func (cg *ChaincodeGroup) Version() string {
return cg.protos.ChaincodeIdentifier.Version
}

// Validation returns how to validate transactions for this chaincode.
// The string returned is the name of the validation method (usually 'vscc')
// and the bytes returned are the argument to the validation (in the case of
// 'vscc', this is a marshaled pb.VSCCArgs message).
func (cg *ChaincodeGroup) Validation() (string, []byte) {
return cg.protos.ChaincodeValidation.Name, cg.protos.ChaincodeValidation.Argument
}

// Endorsement returns how to endorse proposals for this chaincode.
// The string returns is the name of the endorsement method (usually 'escc').
func (cg *ChaincodeGroup) Endorsement() string {
return cg.protos.ChaincodeEndorsement.Name
}

func newChaincodeGroup(name string, group *cb.ConfigGroup) (*ChaincodeGroup, error) {
if len(group.Groups) > 0 {
return nil, errors.New("chaincode group does not support sub-groups")
}

protos := &chaincodeProtos{}
if err := channelconfig.DeserializeProtoValuesFromGroup(group, protos); err != nil {
logger.Panicf("Programming error in structure definition: %s", err)
}

return &ChaincodeGroup{
name: name,
protos: protos,
}, nil
}
78 changes: 78 additions & 0 deletions common/resourcesconfig/chaincode_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
Copyright IBM Corp. 2017 All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package resourcesconfig

import (
"testing"

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

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

const (
sampleChaincodeVersion = "foo"
sampleChaincodeValidationName = "foobar"
sampleChaincodeEndorsementName = "barfoo"
)

var (
sampleChaincodeHash = []byte("bar")
sampleChaincodeValidationArg = []byte("foobararg")
)

var sampleChaincodeGroup = &cb.ConfigGroup{
Values: map[string]*cb.ConfigValue{
"ChaincodeIdentifier": &cb.ConfigValue{
Value: utils.MarshalOrPanic(&pb.ChaincodeIdentifier{
Version: sampleChaincodeVersion,
Hash: sampleChaincodeHash,
}),
},
"ChaincodeValidation": &cb.ConfigValue{
Value: utils.MarshalOrPanic(&pb.ChaincodeValidation{
Name: sampleChaincodeValidationName,
Argument: sampleChaincodeValidationArg,
}),
},
"ChaincodeEndorsement": &cb.ConfigValue{
Value: utils.MarshalOrPanic(&pb.ChaincodeEndorsement{
Name: sampleChaincodeEndorsementName,
}),
},
},
}

func TestGreenChaincodePath(t *testing.T) {
ccg, err := newChaincodeGroup(sampleChaincodeName, sampleChaincodeGroup)
assert.NotNil(t, ccg)
assert.NoError(t, err)

assert.Equal(t, sampleChaincodeName, ccg.Name())
assert.Equal(t, sampleChaincodeVersion, ccg.Version())
assert.Equal(t, sampleChaincodeHash, ccg.Hash())

validationName, validationArg := ccg.Validation()
assert.Equal(t, sampleChaincodeValidationName, validationName)
assert.Equal(t, sampleChaincodeValidationArg, validationArg)

assert.Equal(t, sampleChaincodeEndorsementName, ccg.Endorsement())
}

func TestBadSubgroupsChaincodeGroup(t *testing.T) {
ccg, err := newChaincodeGroup("bar", &cb.ConfigGroup{
Groups: map[string]*cb.ConfigGroup{
"subGroup": &cb.ConfigGroup{},
},
})

assert.Nil(t, ccg)
assert.Error(t, err)
assert.Regexp(t, "chaincode group does not support sub-groups", err.Error())
}
Loading

0 comments on commit ecabe49

Please sign in to comment.