Skip to content

Commit

Permalink
Merge "FABG-909 - Multi-org channel config update"
Browse files Browse the repository at this point in the history
  • Loading branch information
Aleksandar Likic authored and Gerrit Code Review committed Sep 17, 2019
2 parents ba370d2 + fc08b96 commit 1898707
Show file tree
Hide file tree
Showing 7 changed files with 269 additions and 215 deletions.
39 changes: 0 additions & 39 deletions pkg/client/resmgmt/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ import (
"path/filepath"
"time"

"github.com/hyperledger/fabric-sdk-go/pkg/util/test"

"github.com/golang/protobuf/proto"

"github.com/hyperledger/fabric-protos-go/common"
Expand Down Expand Up @@ -249,43 +247,6 @@ func ExampleClient_SaveChannel_withOrdererEndpoint() {

}

func ExampleClient_UpdateChannelConfig_success() {

c, err := New(mockClientProvider())
if err != nil {
fmt.Printf("failed to create client: %s\n", err)
}

block, err := c.QueryConfigBlockFromOrderer("mychannel", WithOrdererEndpoint("example.com"))
if err != nil {
fmt.Printf("QueryConfigBlockFromOrderer returned error: %s\n", err)
}
channelConfig, err := resource.ExtractConfigFromBlock(block)
if err != nil {
fmt.Println("extractConfigFromBlock failed")
}

// Modify channel configuration
_, err = test.ModifyMaxMessageCount(channelConfig)
if err != nil {
fmt.Printf("error modifying channel configuration: %s\n", err)
}

resp, err := c.UpdateChannelConfig(UpdateChannelConfigRequest{ChannelID: "mychannel", ChannelConfig: channelConfig}, WithOrdererEndpoint("example.com"))
if err != nil {
fmt.Printf("failed to update channel config: %s\n", err)
}

if resp.TransactionID == "" {
fmt.Println("Failed to save channel")
}

fmt.Println("Updated channel config")

// Output: Updated channel config

}

func ExampleClient_JoinChannel() {

c, err := New(mockClientProvider())
Expand Down
103 changes: 16 additions & 87 deletions pkg/client/resmgmt/resmgmt.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,28 +112,18 @@ type requestOptions struct {
//SaveChannelRequest holds parameters for save channel request
type SaveChannelRequest struct {
ChannelID string
ChannelConfig io.Reader // ChannelConfig data source
ChannelConfigPath string // Convenience option to use the named file as ChannelConfig reader
SigningIdentities []msp.SigningIdentity // Users that sign channel configuration
ChannelConfig io.Reader // ChannelConfig data source
ChannelConfigPath string // Convenience option to use the named file as ChannelConfig reader
// Users that sign channel configuration
// deprecated - one entity shouldn't have access to another entities' keys to sign on their behalf
SigningIdentities []msp.SigningIdentity
}

// SaveChannelResponse contains response parameters for save channel
type SaveChannelResponse struct {
TransactionID fab.TransactionID
}

// UpdateChannelConfigRequest holds parameters for update channel config request.
type UpdateChannelConfigRequest struct {
ChannelID string
ChannelConfig *common.Config // Desired channel config.
SigningIdentities []msp.SigningIdentity // Users that sign channel configuration
}

// UpdateChannelConfigResponse contains response parameters for update channel config
type UpdateChannelConfigResponse struct {
TransactionID fab.TransactionID
}

//RequestOption func for each Opts argument
type RequestOption func(ctx context.Client, opts *requestOptions) error

Expand Down Expand Up @@ -971,54 +961,6 @@ func (rc *Client) SaveChannel(req SaveChannelRequest, options ...RequestOption)
return SaveChannelResponse{TransactionID: txID}, nil
}

// UpdateChannelConfig updates channel configuration.
// Parameters:
// req holds info about mandatory channel name and configuration
// options holds optional request options
// if options have signatures (WithConfigSignatures() or 1 or more WithConfigSignature() calls), then UpdateChannelConfig will
// use these signatures instead of creating ones for the SigningIdentities found in req.
// Make sure that req.ChannelConfig has the channel config matching these signatures.
//
// Returns:
// update channel config response with transaction ID
func (rc *Client) UpdateChannelConfig(req UpdateChannelConfigRequest, options ...RequestOption) (UpdateChannelConfigResponse, error) {

opts, err := rc.prepareRequestOpts(options...)
if err != nil {
return UpdateChannelConfigResponse{}, err
}

err = rc.validateUpdateChannelConfigRequest(req)
if err != nil {
return UpdateChannelConfigResponse{}, err
}

logger.Debugf("updating channel config: %s", req.ChannelID)

orderer, err := rc.requestOrderer(&opts, req.ChannelID)
if err != nil {
return UpdateChannelConfigResponse{}, errors.WithMessage(err, "failed to find orderer for request")
}

chConfig, err := rc.calculateConfigUpdate(req, orderer)
if err != nil {
return UpdateChannelConfigResponse{}, errors.WithMessage(err, "prepare channel ConfigTx failed")
}

txID, err := rc.signAndSubmitChannelConfigTx(
req.ChannelID,
req.SigningIdentities,
opts,
chConfig,
orderer,
)
if err != nil {
return UpdateChannelConfigResponse{}, errors.WithMessage(err, "update channel config failed")
}

return UpdateChannelConfigResponse{TransactionID: txID}, nil
}

func (rc *Client) signAndSubmitChannelConfigTx(channelID string, signingIdentities []msp.SigningIdentity, opts requestOptions, chConfigTx []byte, orderer fab.Orderer) (fab.TransactionID, error) {
var configSignatures []*common.ConfigSignature
var err error
Expand Down Expand Up @@ -1048,28 +990,23 @@ func (rc *Client) signAndSubmitChannelConfigTx(channelID string, signingIdentiti
return txID, nil
}

func (rc *Client) calculateConfigUpdate(req UpdateChannelConfigRequest, orderer fab.Orderer) ([]byte, error) {
block, err := rc.QueryConfigBlockFromOrderer(req.ChannelID, WithOrderer(orderer))
if err != nil {
return nil, errors.WithMessage(err, "retrieving current channel config failed")
}
currentConfig, err := resource.ExtractConfigFromBlock(block)
if err != nil {
return nil, errors.WithMessage(err, "extracting config from the latest block failed")
// CalculateConfigUpdate calculates channel config update based on the difference between provided
// current channel config and proposed new channel config.
func CalculateConfigUpdate(channelID string, currentConfig, newConfig *common.Config) (*common.ConfigUpdate, error) {

if channelID == "" || currentConfig == nil || newConfig == nil {
return nil, errors.New("must provide channel ID and current and new channel config")
}
if currentConfig.Sequence != req.ChannelConfig.Sequence {

if currentConfig.Sequence != newConfig.Sequence {
return nil, errors.New("channel config sequence mismatch")
}
configUpdate, err := update.Compute(currentConfig, req.ChannelConfig)
configUpdate, err := update.Compute(currentConfig, newConfig)
if err != nil {
return nil, errors.WithMessage(err, "config update computation failed")
}
configUpdate.ChannelId = req.ChannelID
configUpdateBytes, err := proto.Marshal(configUpdate)
if err != nil {
return nil, errors.WithMessage(err, "marshalling config update failed")
}
return configUpdateBytes, nil
configUpdate.ChannelId = channelID
return configUpdate, nil
}

func (rc *Client) validateSaveChannelRequest(req SaveChannelRequest) error {
Expand All @@ -1080,14 +1017,6 @@ func (rc *Client) validateSaveChannelRequest(req SaveChannelRequest) error {
return nil
}

func (rc *Client) validateUpdateChannelConfigRequest(req UpdateChannelConfigRequest) error {

if req.ChannelID == "" || req.ChannelConfig == nil {
return errors.New("must provide channel ID and channel config")
}
return nil
}

func (rc *Client) getConfigSignatures(signingIdentities []msp.SigningIdentity, chConfig []byte) ([]*common.ConfigSignature, error) {
// Signing user has to belong to one of configured channel organisations
// In case that order org is one of channel orgs we can use context user
Expand Down
53 changes: 15 additions & 38 deletions pkg/client/resmgmt/resmgmt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import (
"testing"
"time"

"github.com/hyperledger/fabric-protos-go/orderer"

"github.com/hyperledger/fabric-sdk-go/pkg/util/test"

"github.com/hyperledger/fabric-sdk-go/pkg/common/errors/retry"
Expand Down Expand Up @@ -1075,40 +1077,16 @@ func getNetworkConfigWithoutOrderer(t *testing.T) fab.EndpointConfig {
return config
}

func TestUpdateChannelConfigSuccess(t *testing.T) {
ctx := setupTestContext("test", "Org1MSP")
func TestCalculateConfigUpdate(t *testing.T) {

// Create mock orderer with simple mock block
orderer := fcmocks.NewMockOrderer("", nil)
//defer orderer.CloseQueue()

setupCustomOrderer(ctx, orderer)
cc := setupResMgmtClient(t, ctx)

// Test valid Save Channel request using config block (success)
// Get the original configuration
originalConfiglBlockBytes, err := ioutil.ReadFile(filepath.Join("testdata", "config.block"))
assert.Nil(t, err, "opening config.block file failed")
originalConfigBlock := &common.Block{}
assert.Nil(t, proto.Unmarshal(originalConfiglBlockBytes, originalConfigBlock), "unmarshalling originalConfigBlock failed")
originalConfig, err := resource.ExtractConfigFromBlock(originalConfigBlock)
assert.Nil(t, err, "extractConfigFromBlock failed")

orderer.EnqueueForSendDeliver(
// The first call to orderer returns the very last block
// For testing, we can put here any valid block.
originalConfigBlock,
common.Status_SUCCESS,
)
orderer.EnqueueForSendDeliver(
// The next call returns the last configuration block,
// which is the input to config update tx calculation
originalConfigBlock,
common.Status_SUCCESS,
)
resp, err := cc.UpdateChannelConfig(UpdateChannelConfigRequest{ChannelID: "mychannel", ChannelConfig: originalConfig}, WithOrderer(orderer))
assert.NotNil(t, err, "Should have failed for unchanged configuration")
assert.Contains(t, err.Error(), "no differences detected between original and updated config")

// Prepare new configuration
modifiedConfigBytes, err := proto.Marshal(originalConfig)
assert.Nil(t, err, "error marshalling originalConfig")
Expand All @@ -1118,19 +1096,18 @@ func TestUpdateChannelConfigSuccess(t *testing.T) {
assert.Nil(t, err, "error modifying config")
assert.Nil(t, test.VerifyMaxMessageCount(modifiedConfig, newMaxMessageCount), "error verifying modified config")

orderer.EnqueueForSendDeliver(
originalConfigBlock,
common.Status_SUCCESS,
)
orderer.EnqueueForSendDeliver(
originalConfigBlock,
common.Status_SUCCESS,
)
resp, err = cc.UpdateChannelConfig(UpdateChannelConfigRequest{ChannelID: "mychannel", ChannelConfig: modifiedConfig}, WithOrderer(orderer))
assert.Nil(t, err, "error should be nil")
assert.NotEmpty(t, resp.TransactionID, "transaction ID should be populated")
channelID := "mychannel"

configUpdate, err := CalculateConfigUpdate(channelID, originalConfig, modifiedConfig)
assert.NoError(t, err, "calculating config update failed")
assert.NotNil(t, configUpdate, "calculated config update is nil")
assert.Equal(t, channelID, configUpdate.ChannelId, "channel ID mismatch")

updatedBatchSizeBytes := configUpdate.WriteSet.Groups["Orderer"].Values["BatchSize"].Value
batchSize := &orderer.BatchSize{}
assert.Nil(t, proto.Unmarshal(updatedBatchSizeBytes, batchSize), "unmarshalling BatchSize failed")
assert.Equal(t, newMaxMessageCount, batchSize.MaxMessageCount, "MaxMessageCount mismatch")

orderer.CloseQueue()
}

func TestSaveChannelSuccess(t *testing.T) {
Expand Down
47 changes: 47 additions & 0 deletions pkg/util/test/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,58 @@ package test
import (
"fmt"

"github.com/hyperledger/fabric-protos-go/peer"

"github.com/pkg/errors"

"github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric/protoutil"

"github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric-protos-go/common"
"github.com/hyperledger/fabric-protos-go/orderer"
)

// AddACL adds an ACL config value to channel config
func AddACL(config *common.Config, policyName, policy string) error {

aclsConfigValue, ok := config.ChannelGroup.Groups["Application"].Values["ACLs"]
if !ok {
return errors.New("ACL missing from Application config")
}
acls := &peer.ACLs{}
err := proto.Unmarshal(aclsConfigValue.Value, acls)
if err != nil {
return err
}
acls.Acls[policyName] = &peer.APIResource{PolicyRef: policy}
aclsConfigValue.Value = protoutil.MarshalOrPanic(acls)

return nil
}

// VerifyACL verifies an ACL config value
func VerifyACL(config *common.Config, expectedPolicyName, expectedPolicy string) error {

aclsConfigValue, ok := config.ChannelGroup.Groups["Application"].Values["ACLs"]
if !ok {
return errors.New("ACL missing from Application config")
}
acls := &peer.ACLs{}
err := proto.Unmarshal(aclsConfigValue.Value, acls)
if err != nil {
return err
}
resource, ok := acls.Acls[expectedPolicyName]
if !ok {
return errors.Errorf("missing expected policy name: %s", expectedPolicyName)
}
if resource.PolicyRef != expectedPolicy {
return errors.Errorf("unexpected policy ref: %s, expected: %s", resource.PolicyRef, expectedPolicy)
}

return nil
}

// ModifyMaxMessageCount increments the orderer's BatchSize.MaxMessageCount in a channel config
func ModifyMaxMessageCount(config *common.Config) (uint32, error) {

Expand Down
Loading

0 comments on commit 1898707

Please sign in to comment.