Skip to content

Commit

Permalink
FABG-891 - Retrieve channel config block
Browse files Browse the repository at this point in the history
Added ability to retrieve channel configuration as a raw block.

Change-Id: Iac3dacc712082f48319e299836e414c6fdba322c
Signed-off-by: Aleksandar Likic <aleksandar.likic@securekey.com>
  • Loading branch information
Aleksandar Likic committed Aug 8, 2019
1 parent 6a18b2f commit 267a181
Show file tree
Hide file tree
Showing 8 changed files with 174 additions and 8 deletions.
42 changes: 36 additions & 6 deletions pkg/client/resmgmt/resmgmt.go
Expand Up @@ -1183,26 +1183,56 @@ func loggedClose(c io.Closer) {
}
}

// QueryConfigFromOrderer config returns channel configuration from orderer. If orderer is not provided using options it will be defaulted to channel orderer (if configured) or random orderer from configuration.
func (rc *Client) getChannelConfig(channelID string, opts requestOptions) (*chconfig.ChannelConfig, error) {
orderer, err := rc.requestOrderer(&opts, channelID)
if err != nil {
return nil, errors.WithMessage(err, "failed to find orderer for request")
}

return chconfig.New(channelID, chconfig.WithOrderer(orderer))
}

// QueryConfigBlockFromOrderer config returns channel configuration block from orderer. If orderer is not provided using options it will be defaulted to channel orderer (if configured) or random orderer from configuration.
// Parameters:
// channelID is mandatory channel ID
// options holds optional request options
//
// Returns:
// channel configuration
func (rc *Client) QueryConfigFromOrderer(channelID string, options ...RequestOption) (fab.ChannelCfg, error) {
// channel configuration block
func (rc *Client) QueryConfigBlockFromOrderer(channelID string, options ...RequestOption) (*common.Block, error) {

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

orderer, err := rc.requestOrderer(&opts, channelID)
channelConfig, err := rc.getChannelConfig(channelID, opts)
if err != nil {
return nil, errors.WithMessage(err, "failed to find orderer for request")
return nil, errors.WithMessage(err, "QueryConfig failed")
}

reqCtx, cancel := rc.createRequestContext(opts, fab.OrdererResponse)
defer cancel()

return channelConfig.QueryBlock(reqCtx)

}

// QueryConfigFromOrderer config returns channel configuration from orderer. If orderer is not provided using options it will be defaulted to channel orderer (if configured) or random orderer from configuration.
// Parameters:
// channelID is mandatory channel ID
// options holds optional request options
//
// Returns:
// channel configuration
func (rc *Client) QueryConfigFromOrderer(channelID string, options ...RequestOption) (fab.ChannelCfg, error) {

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

channelConfig, err := chconfig.New(channelID, chconfig.WithOrderer(orderer))
channelConfig, err := rc.getChannelConfig(channelID, opts)
if err != nil {
return nil, errors.WithMessage(err, "QueryConfig failed")
}
Expand Down
3 changes: 3 additions & 0 deletions pkg/common/providers/fab/channel.go
Expand Up @@ -25,6 +25,9 @@ type ChannelConfig interface {

// Query channel configuration
Query(reqCtx reqContext.Context) (ChannelCfg, error)

// QueryBlock queries channel configuration block
QueryBlock(reqCtx reqContext.Context) (*common.Block, error)
}

// ConfigGroupKey is the config group key
Expand Down
29 changes: 27 additions & 2 deletions pkg/fab/chconfig/chconfig.go
Expand Up @@ -138,6 +138,16 @@ func New(channelID string, options ...Option) (*ChannelConfig, error) {
return &ChannelConfig{channelID: channelID, opts: opts}, nil
}

// QueryBlock returns channel configuration
func (c *ChannelConfig) QueryBlock(reqCtx reqContext.Context) (*common.Block, error) {

if c.opts.Orderer != nil {
return c.queryBlockFromOrderer(reqCtx)
}

return c.queryBlockFromPeers(reqCtx)
}

// Query returns channel configuration
func (c *ChannelConfig) Query(reqCtx reqContext.Context) (fab.ChannelCfg, error) {

Expand All @@ -149,6 +159,16 @@ func (c *ChannelConfig) Query(reqCtx reqContext.Context) (fab.ChannelCfg, error)
}

func (c *ChannelConfig) queryPeers(reqCtx reqContext.Context) (*ChannelCfg, error) {
block, err := c.queryBlockFromPeers(reqCtx)

if err != nil {
return nil, errors.WithMessage(err, "QueryBlockConfig failed")
}
return extractConfig(c.channelID, block)

}

func (c *ChannelConfig) queryBlockFromPeers(reqCtx reqContext.Context) (*common.Block, error) {
ctx, ok := contextImpl.RequestClientContext(reqCtx)
if !ok {
return nil, errors.New("failed get client context from reqContext for signPayload")
Expand Down Expand Up @@ -188,7 +208,7 @@ func (c *ChannelConfig) queryPeers(reqCtx reqContext.Context) (*ChannelCfg, erro
if err != nil {
return nil, errors.WithMessage(err, "QueryBlockConfig failed")
}
return extractConfig(c.channelID, block.(*common.Block))
return block.(*common.Block), nil

}

Expand Down Expand Up @@ -217,14 +237,19 @@ func (c *ChannelConfig) calculateTargetsFromConfig(ctx context.Client) ([]fab.Pr

func (c *ChannelConfig) queryOrderer(reqCtx reqContext.Context) (*ChannelCfg, error) {

block, err := resource.LastConfigFromOrderer(reqCtx, c.channelID, c.opts.Orderer, resource.WithRetry(c.opts.RetryOpts))
block, err := c.queryBlockFromOrderer(reqCtx)
if err != nil {
return nil, errors.WithMessage(err, "LastConfigFromOrderer failed")
}

return extractConfig(c.channelID, block)
}

func (c *ChannelConfig) queryBlockFromOrderer(reqCtx reqContext.Context) (*common.Block, error) {

return resource.LastConfigFromOrderer(reqCtx, c.channelID, c.opts.Orderer, resource.WithRetry(c.opts.RetryOpts))
}

//resolveOptsFromConfig loads opts from config if not loaded/initialized
func (c *ChannelConfig) resolveOptsFromConfig(ctx context.Client) {

Expand Down
51 changes: 51 additions & 0 deletions pkg/fab/chconfig/chconfig_test.go
Expand Up @@ -10,6 +10,9 @@ import (
"path/filepath"
"testing"

"github.com/hyperledger/fabric-sdk-go/pkg/fab/resource"
"github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/protos/common"

"time"

"github.com/golang/protobuf/proto"
Expand Down Expand Up @@ -49,6 +52,12 @@ func TestChannelConfigWithPeer(t *testing.T) {
reqCtx, cancel := contextImpl.NewRequest(ctx, contextImpl.WithTimeout(10*time.Second))
defer cancel()

block, err := channelConfig.QueryBlock(reqCtx)
if err != nil {
t.Fatalf(err.Error())
}
checkConfigBlock(t, block)

cfg, err := channelConfig.Query(reqCtx)
if err != nil {
t.Fatalf(err.Error())
Expand All @@ -59,6 +68,17 @@ func TestChannelConfigWithPeer(t *testing.T) {
}
}

func checkConfigBlock(t *testing.T, block *common.Block) {
if block.Header == nil {
t.Fatal("expected header in block")
}

_, err := resource.CreateConfigEnvelope(block.Data.Data[0])
if err != nil {
t.Fatal("expected envelope in block")
}
}

func TestChannelConfigWithPeerWithRetries(t *testing.T) {

numberOfAttempts := 7
Expand Down Expand Up @@ -89,6 +109,26 @@ func TestChannelConfigWithPeerWithRetries(t *testing.T) {
t.Fatalf("Failed to create new channel client: %s", err)
}

// Test QueryBlock
// ---------------

//Set custom retry handler for tracking number of attempts
queryBlockRetryHandler := retry.New(defRetryOpts)
overrideRetryHandler = &customRetryHandler{handler: queryBlockRetryHandler, retries: 0}

queryBlockReqCtx, cancel := contextImpl.NewRequest(ctx, contextImpl.WithTimeout(100*time.Second))
defer cancel()

_, err = channelConfig.QueryBlock(queryBlockReqCtx)
if err == nil || !strings.Contains(err.Error(), "ENDORSEMENT_MISMATCH") {
t.Fatal("Supposed to fail with ENDORSEMENT_MISMATCH. Description: payloads for config block do not match")
}

assert.True(t, overrideRetryHandler.(*customRetryHandler).retries-1 == numberOfAttempts, "number of attempts missmatching")

// Test Query
// ----------

//Set custom retry handler for tracking number of attempts
retryHandler := retry.New(defRetryOpts)
overrideRetryHandler = &customRetryHandler{handler: retryHandler, retries: 0}
Expand Down Expand Up @@ -117,6 +157,11 @@ func TestChannelConfigWithPeerError(t *testing.T) {
reqCtx, cancel := contextImpl.NewRequest(ctx, contextImpl.WithTimeout(10*time.Second))
defer cancel()

_, err = channelConfig.QueryBlock(reqCtx)
if err == nil {
t.Fatal("Should have failed with since there's one endorser and at least two are required")
}

_, err = channelConfig.Query(reqCtx)
if err == nil {
t.Fatal("Should have failed with since there's one endorser and at least two are required")
Expand All @@ -136,6 +181,12 @@ func TestChannelConfigWithOrdererError(t *testing.T) {
reqCtx, cancel := contextImpl.NewRequest(ctx, contextImpl.WithTimeout(1*time.Second))
defer cancel()

// Expecting error since orderer is not setup
_, err = channelConfig.QueryBlock(reqCtx)
if err == nil {
t.Fatal("Should have failed since orderer is not available")
}

// Expecting error since orderer is not setup
_, err = channelConfig.Query(reqCtx)
if err == nil {
Expand Down
7 changes: 7 additions & 0 deletions pkg/fab/mocks/mockchconfig.go
Expand Up @@ -9,6 +9,8 @@ package mocks
import (
reqContext "context"

"github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/protos/common"

"github.com/hyperledger/fabric-sdk-go/pkg/common/providers/context"
"github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab"
msp "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/protos/msp"
Expand Down Expand Up @@ -93,3 +95,8 @@ func NewMockChannelConfig(ctx context.Client, channelID string) (*MockChannelCon
func (c *MockChannelConfig) Query(reqCtx reqContext.Context) (fab.ChannelCfg, error) {
return NewMockChannelCfg(c.channelID), nil
}

// QueryBlock mockcore query for channel configuration block
func (c *MockChannelConfig) QueryBlock(reqCtx reqContext.Context) (*common.Block, error) {
return &common.Block{}, nil
}
9 changes: 9 additions & 0 deletions test/integration/base_test_setup.go
Expand Up @@ -397,6 +397,15 @@ func WaitForOrdererConfigUpdate(t *testing.T, client *resmgmt.Client, channelID
if currentBlock <= lastConfigBlock && !genesis {
return nil, status.New(status.TestStatus, status.GenericTransient.ToInt32(), fmt.Sprintf("Block number was not incremented [%d, %d]", currentBlock, lastConfigBlock), nil)
}

block, err := client.QueryConfigBlockFromOrderer(channelID, resmgmt.WithOrdererEndpoint("orderer.example.com"))
if err != nil {
return nil, status.New(status.TestStatus, status.GenericTransient.ToInt32(), err.Error(), nil)
}
if block.Header.Number != currentBlock {
return nil, status.New(status.TestStatus, status.GenericTransient.ToInt32(), fmt.Sprintf("Invalid block number [%d, %d]", block.Header.Number, currentBlock), nil)
}

return &currentBlock, nil
},
)
Expand Down
12 changes: 12 additions & 0 deletions test/integration/pkg/client/resmgmt/resmgmt_queries_test.go
Expand Up @@ -113,12 +113,24 @@ func testQueryConfigFromOrderer(t *testing.T, channelID string, client *resmgmt.
if !contains(channelCfg.Orderers(), expected) {
t.Fatalf("Expected orderer %s, got %s", expected, channelCfg.Orderers())
}
block, err := client.QueryConfigBlockFromOrderer(channelID, resmgmt.WithOrdererEndpoint("orderer.example.com"))
if err != nil {
t.Fatalf("QueryConfigBlockFromOrderer return error: %s", err)
}
if block.Header.Number != channelCfg.BlockNumber() {
t.Fatalf("QueryConfigBlockFromOrderer returned invalid block number: [%d, %d]", block.Header.Number, channelCfg.BlockNumber())
}

_, err = client.QueryConfigFromOrderer(channelID, resmgmt.WithOrdererEndpoint("non-existent"), resmgmt.WithRetry(retry.DefaultResMgmtOpts))
if err == nil {
t.Fatal("QueryConfig should have failed for invalid orderer")
}

_, err = client.QueryConfigBlockFromOrderer(channelID, resmgmt.WithOrdererEndpoint("non-existent"), resmgmt.WithRetry(retry.DefaultResMgmtOpts))
if err == nil {
t.Fatal("QueryConfigBlockFromOrderer should have failed for invalid orderer")
}

}

func contains(list []string, value string) bool {
Expand Down
29 changes: 29 additions & 0 deletions test/integration/pkg/fabsdk/provider/channel_config_test.go
Expand Up @@ -10,6 +10,10 @@ import (
"strings"
"testing"

"github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/protos/common"

"github.com/hyperledger/fabric-sdk-go/pkg/fab/resource"

contextAPI "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/context"
"github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab"
"github.com/hyperledger/fabric-sdk-go/pkg/context"
Expand Down Expand Up @@ -50,6 +54,13 @@ func TestChannelConfig(t *testing.T) {
reqCtx, cancel := context.NewRequest(channelCtx, context.WithTimeoutType(fab.PeerResponse))
defer cancel()

block, err := cfg.QueryBlock(reqCtx)
if err != nil {
t.Fatal(err)
}

checkConfigBlock(t, block)

response, err := cfg.Query(reqCtx)
if err != nil {
t.Fatal(err)
Expand All @@ -70,6 +81,17 @@ func TestChannelConfig(t *testing.T) {

}

func checkConfigBlock(t *testing.T, block *common.Block) {
if block.Header == nil {
t.Fatal("expected header in block")
}

_, err := resource.CreateConfigEnvelope(block.Data.Data[0])
if err != nil {
t.Fatal("expected envelope in block")
}
}

func TestChannelConfigWithOrderer(t *testing.T) {

testSetup := integration.BaseSetupImpl{
Expand Down Expand Up @@ -129,6 +151,13 @@ func TestChannelConfigWithOrderer(t *testing.T) {
func queryChannelCfg(channelCtx contextAPI.Channel, cfg fab.ChannelConfig, t *testing.T) {
reqCtx, cancel := context.NewRequest(channelCtx, context.WithTimeoutType(fab.OrdererResponse))
defer cancel()

block, err := cfg.QueryBlock(reqCtx)
if err != nil {
t.Fatal(err)
}
checkConfigBlock(t, block)

response, err := cfg.Query(reqCtx)
if err != nil {
t.Fatal(err)
Expand Down

0 comments on commit 267a181

Please sign in to comment.