diff --git a/README.md b/README.md index d2bcbba9f4..9fb154fc2c 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ You're good to go, happy coding! Check out the examples for usage demonstrations ### Examples - [E2E Test](test/integration/e2e/end_to_end.go): Basic example that uses SDK to query and execute transaction +- [Ledger Query Test](test/integration/fab/channel_ledger_test.go): Basic example that uses SDK to query a channel's underlying ledger - [Multi Org Test](test/integration/orgs/multiple_orgs_test.go): An example that has multiple organisations involved in transaction - [Dynamic Endorser Selection](test/integration/sdk/sdk_provider_test.go): An example that uses dynamic endorser selection (based on chaincode policy) - [E2E PKCS11 Test](test/integration/pkcs11/e2e_test.go): E2E Test using a PKCS11 crypto suite and configuration diff --git a/api/apifabclient/channel.go b/api/apifabclient/channel.go index 5f17cc6407..103306c68e 100644 --- a/api/apifabclient/channel.go +++ b/api/apifabclient/channel.go @@ -61,11 +61,11 @@ type Channel interface { // ChannelLedger provides access to the underlying ledger for a channel. type ChannelLedger interface { - QueryInfo(targets []ProposalProcessor) (*common.BlockchainInfo, error) - QueryBlock(blockNumber int, targets []ProposalProcessor) (*common.Block, error) - QueryBlockByHash(blockHash []byte, targets []ProposalProcessor) (*common.Block, error) - QueryTransaction(transactionID string, targets []ProposalProcessor) (*pb.ProcessedTransaction, error) - QueryInstantiatedChaincodes(targets []ProposalProcessor) (*pb.ChaincodeQueryResponse, error) + QueryInfo(targets []ProposalProcessor) ([]*common.BlockchainInfo, error) + QueryBlock(blockNumber int, targets []ProposalProcessor) ([]*common.Block, error) + QueryBlockByHash(blockHash []byte, targets []ProposalProcessor) ([]*common.Block, error) + QueryTransaction(transactionID string, targets []ProposalProcessor) ([]*pb.ProcessedTransaction, error) + QueryInstantiatedChaincodes(targets []ProposalProcessor) ([]*pb.ChaincodeQueryResponse, error) } // OrgAnchorPeer contains information about an anchor peer on this channel diff --git a/api/apifabclient/context.go b/api/apifabclient/context.go index 012fcdf3cd..f72744a8b3 100644 --- a/api/apifabclient/context.go +++ b/api/apifabclient/context.go @@ -32,6 +32,7 @@ type ChannelProvider interface { // ChannelService supplies services related to a channel. type ChannelService interface { ChannelConfig() (ChannelConfig, error) + Ledger() (ChannelLedger, error) Channel() (Channel, error) EventHub() (EventHub, error) // TODO support new event delivery } diff --git a/api/apifabclient/resource.go b/api/apifabclient/resource.go index abd37e5e1a..60c5b6ebed 100644 --- a/api/apifabclient/resource.go +++ b/api/apifabclient/resource.go @@ -15,8 +15,8 @@ import ( type Resource interface { CreateChannel(request CreateChannelRequest) (TransactionID, error) InstallChaincode(request InstallChaincodeRequest) ([]*TransactionProposalResponse, string, error) - QueryInstalledChaincodes(peer Peer) (*pb.ChaincodeQueryResponse, error) - QueryChannels(peer Peer) (*pb.ChannelQueryResponse, error) + QueryInstalledChaincodes(peer ProposalProcessor) (*pb.ChaincodeQueryResponse, error) + QueryChannels(peer ProposalProcessor) (*pb.ChannelQueryResponse, error) GenesisBlockFromOrderer(channelName string, orderer Orderer) (*common.Block, error) JoinChannel(request JoinChannelRequest) error diff --git a/pkg/fabric-client/channel/channel.go b/pkg/fabric-client/channel/channel.go index 98afbdfd89..4771cea923 100644 --- a/pkg/fabric-client/channel/channel.go +++ b/pkg/fabric-client/channel/channel.go @@ -331,7 +331,11 @@ func resolveOrdererURL(ordererURL string) string { // (height, known peers). // This query will be made to the primary peer. func (c *Channel) QueryInfo() (*common.BlockchainInfo, error) { - l := NewLedger(c.clientContext, c.name) + l, err := NewLedger(c.clientContext, c.name) + if err != nil { + return nil, errors.WithMessage(err, "ledger client creation failed") + } + resps, err := l.QueryInfo([]fab.ProposalProcessor{c.PrimaryPeer()}) if err != nil { return nil, err @@ -343,7 +347,11 @@ func (c *Channel) QueryInfo() (*common.BlockchainInfo, error) { // This query will be made to the primary peer. // Returns the block. func (c *Channel) QueryBlockByHash(blockHash []byte) (*common.Block, error) { - l := NewLedger(c.clientContext, c.name) + l, err := NewLedger(c.clientContext, c.name) + if err != nil { + return nil, errors.WithMessage(err, "ledger client creation failed") + } + resps, err := l.QueryBlockByHash(blockHash, []fab.ProposalProcessor{c.PrimaryPeer()}) if err != nil { return nil, err @@ -356,7 +364,11 @@ func (c *Channel) QueryBlockByHash(blockHash []byte) (*common.Block, error) { // blockNumber: The number which is the ID of the Block. // It returns the block. func (c *Channel) QueryBlock(blockNumber int) (*common.Block, error) { - l := NewLedger(c.clientContext, c.name) + l, err := NewLedger(c.clientContext, c.name) + if err != nil { + return nil, errors.WithMessage(err, "ledger client creation failed") + } + resps, err := l.QueryBlock(blockNumber, []fab.ProposalProcessor{c.PrimaryPeer()}) if err != nil { return nil, err @@ -369,7 +381,11 @@ func (c *Channel) QueryBlock(blockNumber int) (*common.Block, error) { // Returns the ProcessedTransaction information containing the transaction. // TODO: add optional target func (c *Channel) QueryTransaction(transactionID string) (*pb.ProcessedTransaction, error) { - l := NewLedger(c.clientContext, c.name) + l, err := NewLedger(c.clientContext, c.name) + if err != nil { + return nil, errors.WithMessage(err, "ledger client creation failed") + } + resps, err := l.QueryTransaction(transactionID, []fab.ProposalProcessor{c.PrimaryPeer()}) if err != nil { return nil, err @@ -380,7 +396,11 @@ func (c *Channel) QueryTransaction(transactionID string) (*pb.ProcessedTransacti // QueryInstantiatedChaincodes queries the instantiated chaincodes on this channel. // This query will be made to the primary peer. func (c *Channel) QueryInstantiatedChaincodes() (*pb.ChaincodeQueryResponse, error) { - l := NewLedger(c.clientContext, c.name) + l, err := NewLedger(c.clientContext, c.name) + if err != nil { + return nil, errors.WithMessage(err, "ledger client creation failed") + } + resps, err := l.QueryInstantiatedChaincodes([]fab.ProposalProcessor{c.PrimaryPeer()}) if err != nil { return nil, err @@ -392,7 +412,11 @@ func (c *Channel) QueryInstantiatedChaincodes() (*pb.ChaincodeQueryResponse, err // QueryConfigBlock returns the current configuration block for the specified channel. If the // peer doesn't belong to the channel, return error func (c *Channel) QueryConfigBlock(peers []fab.Peer, minResponses int) (*common.ConfigEnvelope, error) { - l := NewLedger(c.clientContext, c.name) + l, err := NewLedger(c.clientContext, c.name) + if err != nil { + return nil, errors.WithMessage(err, "ledger client creation failed") + } + return l.QueryConfigBlock(peers, minResponses) } diff --git a/pkg/fabric-client/channel/ledger.go b/pkg/fabric-client/channel/ledger.go index 9059ff1870..22758146d6 100644 --- a/pkg/fabric-client/channel/ledger.go +++ b/pkg/fabric-client/channel/ledger.go @@ -32,12 +32,12 @@ type Ledger struct { } // NewLedger constructs a Ledger client for the current context and named channel. -func NewLedger(ctx fab.Context, chName string) *Ledger { +func NewLedger(ctx fab.Context, chName string) (*Ledger, error) { l := Ledger{ ctx: ctx, chName: chName, } - return &l + return &l, nil } // QueryInfo queries for various useful information on the state of the channel diff --git a/pkg/fabric-client/channel/ledger_test.go b/pkg/fabric-client/channel/ledger_test.go index 17b528e781..b1b6b9d553 100644 --- a/pkg/fabric-client/channel/ledger_test.go +++ b/pkg/fabric-client/channel/ledger_test.go @@ -181,5 +181,5 @@ func setupTestLedger() (*Ledger, error) { func setupLedger(channelID string) (*Ledger, error) { user := mocks.NewMockUser("test") ctx := mocks.NewMockContext(user) - return NewLedger(ctx, channelID), nil + return NewLedger(ctx, channelID) } diff --git a/pkg/fabric-client/mocks/mockchprovider.go b/pkg/fabric-client/mocks/mockchprovider.go index 232d66270f..fc875b26c8 100644 --- a/pkg/fabric-client/mocks/mockchprovider.go +++ b/pkg/fabric-client/mocks/mockchprovider.go @@ -68,3 +68,8 @@ func (cs *MockChannelService) Channel() (fab.Channel, error) { func (cs *MockChannelService) ChannelConfig() (fab.ChannelConfig, error) { return nil, nil } + +// Ledger ... +func (cs *MockChannelService) Ledger() (fab.ChannelLedger, error) { + return nil, nil +} diff --git a/pkg/fabric-client/mocks/mockresource.go b/pkg/fabric-client/mocks/mockresource.go index 8530982532..07a078fd73 100644 --- a/pkg/fabric-client/mocks/mockresource.go +++ b/pkg/fabric-client/mocks/mockresource.go @@ -58,7 +58,7 @@ func (c *MockResource) CreateChannel(request fab.CreateChannelRequest) (fab.Tran } //QueryChannels ... -func (c *MockResource) QueryChannels(peer fab.Peer) (*pb.ChannelQueryResponse, error) { +func (c *MockResource) QueryChannels(peer fab.ProposalProcessor) (*pb.ChannelQueryResponse, error) { return nil, errors.New("Not implemented yet") } @@ -77,7 +77,7 @@ func (c *MockResource) JoinChannel(request fab.JoinChannelRequest) error { } //QueryInstalledChaincodes mocks query installed chaincodes -func (c *MockResource) QueryInstalledChaincodes(peer fab.Peer) (*pb.ChaincodeQueryResponse, error) { +func (c *MockResource) QueryInstalledChaincodes(peer fab.ProposalProcessor) (*pb.ChaincodeQueryResponse, error) { if peer == nil { return nil, errors.New("Generate Error") } diff --git a/pkg/fabric-client/resource/resource.go b/pkg/fabric-client/resource/resource.go index c86ed32fdb..5e33e1a837 100644 --- a/pkg/fabric-client/resource/resource.go +++ b/pkg/fabric-client/resource/resource.go @@ -321,7 +321,7 @@ func (c *Resource) createOrUpdateChannel(request fab.CreateChannelRequest, haveE } // QueryChannels queries the names of all the channels that a peer has joined. -func (c *Resource) QueryChannels(peer fab.Peer) (*pb.ChannelQueryResponse, error) { +func (c *Resource) QueryChannels(peer fab.ProposalProcessor) (*pb.ChannelQueryResponse, error) { if peer == nil { return nil, errors.New("peer required") @@ -346,7 +346,7 @@ func (c *Resource) QueryChannels(peer fab.Peer) (*pb.ChannelQueryResponse, error // QueryInstalledChaincodes queries the installed chaincodes on a peer. // Returns the details of all chaincodes installed on a peer. -func (c *Resource) QueryInstalledChaincodes(peer fab.Peer) (*pb.ChaincodeQueryResponse, error) { +func (c *Resource) QueryInstalledChaincodes(peer fab.ProposalProcessor) (*pb.ChaincodeQueryResponse, error) { if peer == nil { return nil, errors.New("peer required") diff --git a/pkg/fabsdk/api/pvdr.go b/pkg/fabsdk/api/pvdr.go index ba89075023..84e17dabca 100644 --- a/pkg/fabsdk/api/pvdr.go +++ b/pkg/fabsdk/api/pvdr.go @@ -15,9 +15,10 @@ import ( // FabricProvider enables access to fabric objects such as peer and user based on config or context. type FabricProvider interface { CreateChannelClient(user apifabclient.IdentityContext, cfg apifabclient.ChannelCfg) (apifabclient.Channel, error) + CreateChannelLedger(ic apifabclient.IdentityContext, name string) (apifabclient.ChannelLedger, error) CreateChannelConfig(user apifabclient.IdentityContext, name string) (apifabclient.ChannelConfig, error) CreateResourceClient(user apifabclient.IdentityContext) (apifabclient.Resource, error) - CreateEventHub(ic apifabclient.IdentityContext, channelID string) (apifabclient.EventHub, error) + CreateEventHub(ic apifabclient.IdentityContext, name string) (apifabclient.EventHub, error) CreateCAClient(orgID string) (apifabca.FabricCAClient, error) CreatePeerFromConfig(peerCfg *apiconfig.NetworkPeer) (apifabclient.Peer, error) diff --git a/pkg/fabsdk/provider/chpvdr/chprovider.go b/pkg/fabsdk/provider/chpvdr/chprovider.go index d85181cf10..7a6962a8be 100644 --- a/pkg/fabsdk/provider/chpvdr/chprovider.go +++ b/pkg/fabsdk/provider/chpvdr/chprovider.go @@ -92,3 +92,8 @@ func (cs *ChannelService) EventHub() (apifabclient.EventHub, error) { func (cs *ChannelService) ChannelConfig() (apifabclient.ChannelConfig, error) { return cs.fabricProvider.CreateChannelConfig(cs.identityContext, cs.cfg.Name()) } + +// Ledger providers a client for the current context and named channel. +func (cs *ChannelService) Ledger() (apifabclient.ChannelLedger, error) { + return cs.fabricProvider.CreateChannelLedger(cs.identityContext, cs.cfg.Name()) +} diff --git a/pkg/fabsdk/provider/fabpvdr/fabpvdr.go b/pkg/fabsdk/provider/fabpvdr/fabpvdr.go index 1abbbaebac..b3b9326deb 100644 --- a/pkg/fabsdk/provider/fabpvdr/fabpvdr.go +++ b/pkg/fabsdk/provider/fabpvdr/fabpvdr.go @@ -64,6 +64,20 @@ func (f *FabricProvider) CreateChannelClient(ic apifabclient.IdentityContext, cf return channel, nil } +// CreateChannelLedger returns a new client initialized for the current instance of the SDK. +func (f *FabricProvider) CreateChannelLedger(ic apifabclient.IdentityContext, channelName string) (apifabclient.ChannelLedger, error) { + ctx := &fabContext{ + ProviderContext: f.providerContext, + IdentityContext: ic, + } + ledger, err := channelImpl.NewLedger(ctx, channelName) + if err != nil { + return nil, errors.WithMessage(err, "NewLedger failed") + } + + return ledger, nil +} + // CreateEventHub initilizes the event hub. func (f *FabricProvider) CreateEventHub(ic apifabclient.IdentityContext, channelID string) (apifabclient.EventHub, error) { peerConfig, err := f.providerContext.Config().ChannelPeers(channelID) diff --git a/pkg/logging/logger.go b/pkg/logging/logger.go index 10a9af8cd1..16182dc4c3 100644 --- a/pkg/logging/logger.go +++ b/pkg/logging/logger.go @@ -55,7 +55,7 @@ func loggerProvider() apilogging.LoggerProvider { // Otherwise the built-in logger is used loggerProviderInstance = modlog.LoggerProvider() logger := loggerProviderInstance.GetLogger(loggerModule) - logger.Info(loggerNotInitializedMsg) + logger.Debug(loggerNotInitializedMsg) }) return loggerProviderInstance } diff --git a/test/integration/base_test_setup.go b/test/integration/base_test_setup.go index 03cd5dcea4..b2719d0961 100644 --- a/test/integration/base_test_setup.go +++ b/test/integration/base_test_setup.go @@ -10,7 +10,6 @@ import ( "os" "path" "testing" - "time" "github.com/hyperledger/fabric-sdk-go/api/apiconfig" fab "github.com/hyperledger/fabric-sdk-go/api/apifabclient" @@ -37,7 +36,6 @@ type BaseSetupImpl struct { ConfigFile string OrgID string ChannelID string - ChainCodeID string Initialized bool ChannelConfig string } @@ -56,8 +54,6 @@ var txArgs = [][]byte{[]byte("move"), []byte("a"), []byte("b"), []byte("1")} var initArgs = [][]byte{[]byte("init"), []byte("a"), []byte("100"), []byte("b"), []byte(ExampleCCInitB)} var upgradeArgs = [][]byte{[]byte("init"), []byte("a"), []byte("100"), []byte("b"), []byte(ExampleCCUpgradeB)} -var resMgmtClient resmgmt.ResourceMgmtClient - // ExampleCCQueryArgs returns example cc query args func ExampleCCQueryArgs() [][]byte { return queryArgs @@ -102,44 +98,14 @@ func (setup *BaseSetupImpl) Initialize(t *testing.T) error { setup.Client = sc // TODO: Review logic for retrieving peers (should this be channel peer only) - channel, err := setup.GetChannel(sdk, setup.Identity, sdk.Config(), chconfig.NewChannelCfg(setup.ChannelID), []string{setup.OrgID}) + channel, err := GetChannel(sdk, setup.Identity, sdk.Config(), chconfig.NewChannelCfg(setup.ChannelID), []string{setup.OrgID}) if err != nil { return errors.Wrapf(err, "create channel (%s) failed: %v", setup.ChannelID) } - // Channel management client is responsible for managing channels (create/update) - chMgmtClient, err := sdk.NewClient(fabsdk.WithUser("Admin"), fabsdk.WithOrg("ordererorg")).ChannelMgmt() - if err != nil { - t.Fatalf("Failed to create new channel management client: %s", err) - } - - // Resource management client is responsible for managing resources (joining channels, install/instantiate/upgrade chaincodes) - resMgmtClient, err = sdk.NewClient(fabsdk.WithUser("Admin")).ResourceMgmt() - if err != nil { - t.Fatalf("Failed to create new resource management client: %s", err) - } - - // Check if primary peer has joined channel - alreadyJoined, err := HasPeerJoinedChannel(sc, channel.PrimaryPeer(), channel.Name()) - if err != nil { - return errors.WithMessage(err, "failed while checking if primary peer has already joined channel") - } - - if !alreadyJoined { - - // Create channel (or update if it already exists) - req := chmgmt.SaveChannelRequest{ChannelID: setup.ChannelID, ChannelConfig: setup.ChannelConfig, SigningIdentity: session} - - if err = chMgmtClient.SaveChannel(req); err != nil { - return errors.WithMessage(err, "SaveChannel failed") - } - - time.Sleep(time.Second * 5) - - if err = resMgmtClient.JoinChannel(setup.ChannelID); err != nil { - return errors.WithMessage(err, "JoinChannel failed") - } - } + targets := []fab.ProposalProcessor{channel.PrimaryPeer()} + req := chmgmt.SaveChannelRequest{ChannelID: setup.ChannelID, ChannelConfig: setup.ChannelConfig, SigningIdentity: session} + InitializeChannel(sdk, setup.OrgID, req, targets) if err := setup.setupEventHub(t, sdk, setup.Identity); err != nil { return err @@ -156,7 +122,7 @@ func (setup *BaseSetupImpl) Initialize(t *testing.T) error { } // Get channel from dynamic info - channel, err = setup.GetChannel(sdk, setup.Identity, sdk.Config(), chCfg, []string{setup.OrgID}) + channel, err = GetChannel(sdk, setup.Identity, sdk.Config(), chCfg, []string{setup.OrgID}) if err != nil { return errors.Wrapf(err, "create channel (%s) failed: %v", setup.ChannelID) } @@ -168,7 +134,7 @@ func (setup *BaseSetupImpl) Initialize(t *testing.T) error { } // GetChannel initializes and returns a channel based on config -func (setup *BaseSetupImpl) GetChannel(sdk *fabsdk.FabricSDK, ic fab.IdentityContext, config apiconfig.Config, chCfg fab.ChannelCfg, orgs []string) (fab.Channel, error) { +func GetChannel(sdk *fabsdk.FabricSDK, ic fab.IdentityContext, config apiconfig.Config, chCfg fab.ChannelCfg, orgs []string) (fab.Channel, error) { channel, err := sdk.FabricProvider().CreateChannelClient(ic, chCfg) if err != nil { @@ -236,27 +202,33 @@ func (setup *BaseSetupImpl) InstallCC(name string, path string, version string, } // GetDeployPath .. -func (setup *BaseSetupImpl) GetDeployPath() string { +func GetDeployPath() string { pwd, _ := os.Getwd() return path.Join(pwd, "../../fixtures/testdata") } // InstallAndInstantiateExampleCC install and instantiate using resource management client -func (setup *BaseSetupImpl) InstallAndInstantiateExampleCC() error { - - if setup.ChainCodeID == "" { - setup.ChainCodeID = GenerateRandomID() - } - - return setup.InstallAndInstantiateCC(setup.ChainCodeID, "github.com/example_cc", "v0", setup.GetDeployPath(), initArgs) +func InstallAndInstantiateExampleCC(sdk *fabsdk.FabricSDK, user fabsdk.IdentityOption, orgName string, chainCodeID string) error { + return InstallAndInstantiateCC(sdk, user, orgName, chainCodeID, "github.com/example_cc", "v0", GetDeployPath(), initArgs) } // InstallAndInstantiateCC install and instantiate using resource management client -func (setup *BaseSetupImpl) InstallAndInstantiateCC(ccName, ccPath, ccVersion, goPath string, ccArgs [][]byte) error { +func InstallAndInstantiateCC(sdk *fabsdk.FabricSDK, user fabsdk.IdentityOption, orgName string, ccName, ccPath, ccVersion, goPath string, ccArgs [][]byte) error { ccPkg, err := packager.NewCCPackage(ccPath, goPath) if err != nil { - return err + return errors.WithMessage(err, "creating chaincode package failed") + } + + mspID, err := sdk.Config().MspID(orgName) + if err != nil { + return errors.WithMessage(err, "looking up MSP ID failed") + } + + // Resource management client is responsible for managing resources (joining channels, install/instantiate/upgrade chaincodes) + resMgmtClient, err := sdk.NewClient(user, fabsdk.WithOrg(orgName)).ResourceMgmt() + if err != nil { + return errors.WithMessage(err, "Failed to create new resource management client") } _, err = resMgmtClient.InstallCC(resmgmt.InstallCCRequest{Name: ccName, Path: ccPath, Version: ccVersion, Package: ccPkg}) @@ -264,12 +236,12 @@ func (setup *BaseSetupImpl) InstallAndInstantiateCC(ccName, ccPath, ccVersion, g return err } - ccPolicy := cauthdsl.SignedByMspMember(setup.Identity.MspID()) + ccPolicy := cauthdsl.SignedByMspMember(mspID) return resMgmtClient.InstantiateCC("mychannel", resmgmt.InstantiateCCRequest{Name: ccName, Path: ccPath, Version: ccVersion, Args: ccArgs, Policy: ccPolicy}) } // CreateAndSendTransactionProposal ... TODO duplicate -func (setup *BaseSetupImpl) CreateAndSendTransactionProposal(channel fab.Channel, chainCodeID string, +func CreateAndSendTransactionProposal(channel fab.Channel, chainCodeID string, fcn string, args [][]byte, targets []fab.ProposalProcessor, transientData map[string][]byte) ([]*fab.TransactionProposalResponse, fab.TransactionID, error) { request := fab.ChaincodeInvokeRequest{ @@ -293,7 +265,7 @@ func (setup *BaseSetupImpl) CreateAndSendTransactionProposal(channel fab.Channel } // CreateAndSendTransaction ... -func (setup *BaseSetupImpl) CreateAndSendTransaction(channel fab.Channel, resps []*fab.TransactionProposalResponse) (*fab.TransactionResponse, error) { +func CreateAndSendTransaction(channel fab.Channel, resps []*fab.TransactionProposalResponse) (*fab.TransactionResponse, error) { tx, err := channel.CreateTransaction(resps) if err != nil { @@ -317,7 +289,7 @@ func (setup *BaseSetupImpl) CreateAndSendTransaction(channel fab.Channel, resps // returns a boolean channel which receives true when the event is complete // and an error channel for errors // TODO - Duplicate -func (setup *BaseSetupImpl) RegisterTxEvent(t *testing.T, txID fab.TransactionID, eventHub fab.EventHub) (chan bool, chan error) { +func RegisterTxEvent(t *testing.T, txID fab.TransactionID, eventHub fab.EventHub) (chan bool, chan error) { done := make(chan bool) fail := make(chan error) diff --git a/test/integration/fab/channel_ledger_test.go b/test/integration/fab/channel_ledger_test.go new file mode 100644 index 0000000000..4fc1ad3a28 --- /dev/null +++ b/test/integration/fab/channel_ledger_test.go @@ -0,0 +1,247 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package fab + +import ( + "path" + "strconv" + "testing" + + fab "github.com/hyperledger/fabric-sdk-go/api/apifabclient" + "github.com/hyperledger/fabric-sdk-go/api/apitxn/chclient" + chmgmt "github.com/hyperledger/fabric-sdk-go/api/apitxn/chmgmtclient" + "github.com/hyperledger/fabric-sdk-go/test/integration" + "github.com/hyperledger/fabric-sdk-go/test/metadata" + + "github.com/hyperledger/fabric-sdk-go/pkg/config" + "github.com/hyperledger/fabric-sdk-go/pkg/fabsdk" + "github.com/pkg/errors" +) + +const ( + sdkConfigFile = "../" + integration.ConfigTestFile + channelConfigFile = "mychannel.tx" + channelID = "mychannel" + orgName = org1Name +) + +func initializeLedgerTests(t *testing.T) (*fabsdk.FabricSDK, []fab.ProposalProcessor) { + sdk, err := fabsdk.New(config.FromFile(sdkConfigFile)) + if err != nil { + t.Fatalf("SDK init failed: %v", err) + } + + session, err := sdk.NewClient(fabsdk.WithUser("Admin"), fabsdk.WithOrg(orgName)).Session() + if err != nil { + t.Fatalf("failed getting admin user session for org: %s", err) + } + + targets, err := integration.CreateProposalProcessors(sdk.Config(), []string{orgName}) + if err != nil { + t.Fatalf("creating peers failed: %v", err) + } + + channelConfig := path.Join("../../../", metadata.ChannelConfigPath, channelConfigFile) + req := chmgmt.SaveChannelRequest{ChannelID: channelID, ChannelConfig: channelConfig, SigningIdentity: session} + err = integration.InitializeChannel(sdk, orgName, req, targets) + if err != nil { + t.Fatalf("failed to ensure channel has been initialized: %s", err) + } + return sdk, targets +} + +func TestLedgerQueries(t *testing.T) { + + // Setup tests with a random chaincode ID. + sdk, targets := initializeLedgerTests(t) + chaincodeID := integration.GenerateRandomID() + if err := integration.InstallAndInstantiateExampleCC(sdk, fabsdk.WithUser("Admin"), orgName, chaincodeID); err != nil { + t.Fatalf("InstallAndInstantiateExampleCC return error: %v", err) + } + + // Get a ledger client. + client := sdk.NewClient(fabsdk.WithUser("Admin"), fabsdk.WithOrg(orgName)) + channelSvc, err := client.ChannelService(channelID) + if err != nil { + t.Fatalf("creating channel service failed: %v", err) + } + ledger, err := channelSvc.Ledger() + if err != nil { + t.Fatalf("creating channel ledger client failed: %v", err) + } + + // Test Query Info - retrieve values before transaction + bciBeforeTx, err := ledger.QueryInfo(targets[0:1]) + if err != nil { + t.Fatalf("QueryInfo return error: %v", err) + } + + // Invoke transaction that changes block state + channel, err := client.Channel(channelID) + if err != nil { + t.Fatalf("creating channel failed: %v", err) + } + + txID, err := changeBlockState(t, channel, chaincodeID) + if err != nil { + t.Fatalf("Failed to change block state (invoke transaction). Return error: %v", err) + } + + // Test Query Info - retrieve values after transaction + bciAfterTx, err := ledger.QueryInfo(targets[0:1]) + if err != nil { + t.Fatalf("QueryInfo return error: %v", err) + } + + // Test Query Info -- verify block size changed after transaction + if (bciAfterTx[0].Height - bciBeforeTx[0].Height) <= 0 { + t.Fatalf("Block size did not increase after transaction") + } + + testQueryTransaction(t, ledger, txID, targets) + + testQueryBlock(t, ledger, targets) + + testInstantiatedChaincodes(t, ledger, targets) + +} + +func changeBlockState(t *testing.T, channel chclient.ChannelClient, chaincodeID string) (string, error) { + + req := chclient.Request{ + ChaincodeID: chaincodeID, + Fcn: "invoke", + Args: integration.ExampleCCQueryArgs(), + } + resp, err := channel.Query(req) + if err != nil { + return "", errors.WithMessage(err, "query funds failed") + } + value := resp.Payload + + // Start transaction that will change block state + txID, err := moveFundsAndGetTxID(t, channel, chaincodeID) + if err != nil { + return "", errors.WithMessage(err, "move funds failed") + } + + resp, err = channel.Query(req) + if err != nil { + return "", errors.WithMessage(err, "query funds failed") + } + valueAfterInvoke := resp.Payload + + // Verify that transaction changed block state + valueInt, _ := strconv.Atoi(string(value)) + valueInt = valueInt + 1 + valueAfterInvokeInt, _ := strconv.Atoi(string(valueAfterInvoke)) + if valueInt != valueAfterInvokeInt { + return "", errors.Errorf("SendTransaction didn't change the QueryValue %s", value) + } + + return txID, nil +} + +func testQueryTransaction(t *testing.T, ledger fab.ChannelLedger, txID string, targets []fab.ProposalProcessor) { + + // Test Query Transaction -- verify that valid transaction has been processed + processedTransactions, err := ledger.QueryTransaction(txID, targets) + if err != nil { + t.Fatalf("QueryTransaction return error: %v", err) + } + + for _, processedTransaction := range processedTransactions { + if processedTransaction.TransactionEnvelope == nil { + t.Fatalf("QueryTransaction failed to return transaction envelope") + } + } + + // Test Query Transaction -- Retrieve non existing transaction + _, err = ledger.QueryTransaction("123ABC", targets) + if err == nil { + t.Fatalf("QueryTransaction non-existing didn't return an error") + } +} + +func testQueryBlock(t *testing.T, ledger fab.ChannelLedger, targets []fab.ProposalProcessor) { + + // Retrieve current blockchain info + bcis, err := ledger.QueryInfo(targets) + if err != nil { + t.Fatalf("QueryInfo return error: %v", err) + } + + for i, bci := range bcis { + // Test Query Block by Hash - retrieve current block by hash + block, err := ledger.QueryBlockByHash(bci.CurrentBlockHash, targets[i:i+1]) + if err != nil { + t.Fatalf("QueryBlockByHash return error: %v", err) + } + + if block[0].Data == nil { + t.Fatalf("QueryBlockByHash block data is nil") + } + } + + // Test Query Block by Hash - retrieve block by non-existent hash + _, err = ledger.QueryBlockByHash([]byte("non-existent"), targets) + if err == nil { + t.Fatalf("QueryBlockByHash non-existent didn't return an error") + } + + // Test Query Block - retrieve block by number + blocks, err := ledger.QueryBlock(1, targets) + if err != nil { + t.Fatalf("QueryBlock return error: %v", err) + } + for _, block := range blocks { + if block.Data == nil { + t.Fatalf("QueryBlock block data is nil") + } + } + + // Test Query Block - retrieve block by non-existent number + _, err = ledger.QueryBlock(2147483647, targets) + if err == nil { + t.Fatalf("QueryBlock non-existent didn't return an error") + } +} + +func testInstantiatedChaincodes(t *testing.T, ledger fab.ChannelLedger, targets []fab.ProposalProcessor) { + + // Test Query Instantiated chaincodes + chaincodeQueryResponses, err := ledger.QueryInstantiatedChaincodes(targets) + if err != nil { + t.Fatalf("QueryInstantiatedChaincodes return error: %v", err) + } + + for _, chaincodeQueryResponse := range chaincodeQueryResponses { + for _, chaincode := range chaincodeQueryResponse.Chaincodes { + t.Logf("**InstantiatedCC: %s", chaincode) + } + } +} + +// MoveFundsAndGetTxID ... +func moveFundsAndGetTxID(t *testing.T, channel chclient.ChannelClient, chaincodeID string) (string, error) { + + transientDataMap := make(map[string][]byte) + transientDataMap["result"] = []byte("Transient data in move funds...") + + req := chclient.Request{ + ChaincodeID: chaincodeID, + Fcn: "invoke", + Args: integration.ExampleCCTxArgs(), + TransientMap: transientDataMap, + } + resp, err := channel.Execute(req) + if err != nil { + return "", errors.WithMessage(err, "execute move funds failed") + } + + return resp.TransactionID.ID, nil +} diff --git a/test/integration/fab/channel_queries_test.go b/test/integration/fab/channel_queries_test.go index 6e81a3b5eb..7cd519e2d9 100644 --- a/test/integration/fab/channel_queries_test.go +++ b/test/integration/fab/channel_queries_test.go @@ -7,177 +7,28 @@ SPDX-License-Identifier: Apache-2.0 package fab import ( - "path" - "strconv" "testing" - "time" "github.com/hyperledger/fabric-sdk-go/api/apiconfig" fab "github.com/hyperledger/fabric-sdk-go/api/apifabclient" + "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/peer" "github.com/hyperledger/fabric-sdk-go/test/integration" - "github.com/hyperledger/fabric-sdk-go/test/metadata" - - peer "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/peer" - "github.com/pkg/errors" ) func TestChannelQueries(t *testing.T) { + chaincodeID := integration.GenerateRandomID() + testSetup := initializeTests(t, chaincodeID) - testSetup := &integration.BaseSetupImpl{ - ConfigFile: "../" + integration.ConfigTestFile, - ChannelID: "mychannel", - OrgID: org1Name, - ChannelConfig: path.Join("../../../", metadata.ChannelConfigPath, "mychannel.tx"), - ConnectEventHub: true, - } - - if err := testSetup.Initialize(t); err != nil { - t.Fatalf(err.Error()) - } - - channel := testSetup.Channel - client := testSetup.Client - - if err := testSetup.InstallAndInstantiateExampleCC(); err != nil { - t.Fatalf("InstallAndInstantiateExampleCC return error: %v", err) - } - - // Test Query Info - retrieve values before transaction - bciBeforeTx, err := channel.QueryInfo() - if err != nil { - t.Fatalf("QueryInfo return error: %v", err) - } - - // Invoke transaction that changes block state - txID, err := changeBlockState(t, testSetup) - if err != nil { - t.Fatalf("Failed to change block state (invoke transaction). Return error: %v", err) - } - - // Test Query Info - retrieve values after transaction - bciAfterTx, err := channel.QueryInfo() - if err != nil { - t.Fatalf("QueryInfo return error: %v", err) - } - - // Test Query Info -- verify block size changed after transaction - if (bciAfterTx.Height - bciBeforeTx.Height) <= 0 { - t.Fatalf("Block size did not increase after transaction") - } - - testQueryTransaction(t, channel, txID) - - testQueryBlock(t, channel) - - testQueryConfigBlock(t, channel) - - testQueryChannels(t, channel, client) - - testInstalledChaincodes(t, channel, client) - - testQueryByChaincode(t, channel, testSetup) - - testInstantiatedChaincodes(t, channel) - -} - -func changeBlockState(t *testing.T, testSetup *integration.BaseSetupImpl) (string, error) { - - tpResponses, _, err := testSetup.CreateAndSendTransactionProposal(testSetup.Channel, testSetup.ChainCodeID, "invoke", integration.ExampleCCQueryArgs(), []fab.ProposalProcessor{testSetup.Channel.PrimaryPeer()}, nil) - if err != nil { - return "", errors.WithMessage(err, "CreateAndSendTransactionProposal failed") - } - - value := tpResponses[0].ProposalResponse.GetResponse().Payload - - // Start transaction that will change block state - txID, err := moveFundsAndGetTxID(t, testSetup) - if err != nil { - return "", errors.WithMessage(err, "move funds failed") - } - - tpResponses, _, err = testSetup.CreateAndSendTransactionProposal(testSetup.Channel, testSetup.ChainCodeID, "invoke", integration.ExampleCCQueryArgs(), []fab.ProposalProcessor{testSetup.Channel.PrimaryPeer()}, nil) - if err != nil { - return "", errors.WithMessage(err, "CreateAndSendTransactionProposal failed") - } - - valueAfterInvoke := tpResponses[0].ProposalResponse.GetResponse().Payload - - // Verify that transaction changed block state - valueInt, _ := strconv.Atoi(string(value)) - valueInt = valueInt + 1 - valueAfterInvokeInt, _ := strconv.Atoi(string(valueAfterInvoke)) - if valueInt != valueAfterInvokeInt { - return "", errors.Errorf("SendTransaction didn't change the QueryValue %s", value) - } - - return txID, nil -} - -func testQueryTransaction(t *testing.T, channel fab.Channel, txID string) { - - // Test Query Transaction -- verify that valid transaction has been processed - processedTransaction, err := channel.QueryTransaction(txID) - if err != nil { - t.Fatalf("QueryTransaction return error: %v", err) - } - - if processedTransaction.TransactionEnvelope == nil { - t.Fatalf("QueryTransaction failed to return transaction envelope") - } - - // Test Query Transaction -- Retrieve non existing transaction - processedTransaction, err = channel.QueryTransaction("123ABC") - if err == nil { - t.Fatalf("QueryTransaction non-existing didn't return an error") - } - -} - -func testQueryBlock(t *testing.T, channel fab.Channel) { - - // Retrieve current blockchain info - bci, err := channel.QueryInfo() - if err != nil { - t.Fatalf("QueryInfo return error: %v", err) - } - - // Test Query Block by Hash - retrieve current block by hash - block, err := channel.QueryBlockByHash(bci.CurrentBlockHash) - if err != nil { - t.Fatalf("QueryBlockByHash return error: %v", err) - } - - if block.Data == nil { - t.Fatalf("QueryBlockByHash block data is nil") - } - - // Test Query Block by Hash - retrieve block by non-existent hash - block, err = channel.QueryBlockByHash([]byte("non-existent")) - if err == nil { - t.Fatalf("QueryBlockByHash non-existent didn't return an error") - } + testQueryConfigBlock(t, testSetup.Channel) - // Test Query Block - retrieve block by number - block, err = channel.QueryBlock(1) - if err != nil { - t.Fatalf("QueryBlock return error: %v", err) - } - - if block.Data == nil { - t.Fatalf("QueryBlock block data is nil") - } + testQueryChannels(t, testSetup.Channel, testSetup.Client) - // Test Query Block - retrieve block by non-existent number - block, err = channel.QueryBlock(2147483647) - if err == nil { - t.Fatalf("QueryBlock non-existent didn't return an error") - } + testInstalledChaincodes(t, testSetup.Channel, testSetup.Client) + testQueryByChaincode(t, testSetup.SDK.Config(), testSetup.Channel) } func testQueryConfigBlock(t *testing.T, channel fab.Channel) { - // Our target will be primary peer on this channel targets := []fab.Peer{channel.PrimaryPeer()} @@ -226,30 +77,8 @@ func testInstalledChaincodes(t *testing.T, channel fab.Channel, client fab.Resou } -func testInstantiatedChaincodes(t *testing.T, channel fab.Channel) { - - // Our target will indirectly be primary peer on this channel - target := channel.PrimaryPeer() - - t.Logf("QueryInstantiatedChaincodes for primary %s", target.URL()) - - // Test Query Instantiated chaincodes - chaincodeQueryResponse, err := channel.QueryInstantiatedChaincodes() - if err != nil { - t.Fatalf("QueryInstantiatedChaincodes return error: %v", err) - } - - for _, chaincode := range chaincodeQueryResponse.Chaincodes { - t.Logf("**InstantiatedCC: %s", chaincode) - } - -} - -func testQueryByChaincode(t *testing.T, channel fab.Channel, testSetup *integration.BaseSetupImpl) { - - config := testSetup.SDK.Config() +func testQueryByChaincode(t *testing.T, config apiconfig.Config, channel fab.Channel) { - // Test valid targets targets := peer.PeersToTxnProcessors(channel.Peers()) request := fab.ChaincodeInvokeRequest{ @@ -327,31 +156,3 @@ func testQueryByChaincode(t *testing.T, channel fab.Channel, testSetup *integrat channel.RemovePeer(firstInvalidTarget) channel.RemovePeer(secondInvalidTarget) } - -// MoveFundsAndGetTxID ... -func moveFundsAndGetTxID(t *testing.T, setup *integration.BaseSetupImpl) (string, error) { - - transientDataMap := make(map[string][]byte) - transientDataMap["result"] = []byte("Transient data in move funds...") - - transactionProposalResponse, txID, err := setup.CreateAndSendTransactionProposal(setup.Channel, setup.ChainCodeID, "invoke", integration.ExampleCCTxArgs(), []fab.ProposalProcessor{setup.Channel.PrimaryPeer()}, transientDataMap) - if err != nil { - return "", errors.WithMessage(err, "CreateAndSendTransactionProposal failed") - } - // Register for commit event - done, fail := setup.RegisterTxEvent(t, txID, setup.EventHub) - - txResponse, err := setup.CreateAndSendTransaction(setup.Channel, transactionProposalResponse) - if err != nil { - return "", errors.WithMessage(err, "CreateAndSendTransaction failed") - } - t.Logf("txResponse: %v", txResponse) - select { - case <-done: - case cerr := <-fail: - return "", errors.Wrapf(cerr, "invoke failed for txid %s", txID) - case <-time.After(time.Second * 30): - return "", errors.Errorf("invoke didn't receive block event for txid %s", txID) - } - return txID.ID, nil -} diff --git a/test/integration/fab/events_test.go b/test/integration/fab/events_test.go index 0d9ba63ecc..527fc70bd8 100644 --- a/test/integration/fab/events_test.go +++ b/test/integration/fab/events_test.go @@ -7,14 +7,12 @@ SPDX-License-Identifier: Apache-2.0 package fab import ( - "path" "sync" "testing" "time" "github.com/hyperledger/fabric-sdk-go/api/apifabclient" "github.com/hyperledger/fabric-sdk-go/test/integration" - "github.com/hyperledger/fabric-sdk-go/test/metadata" "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/protos/common" pb "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/protos/peer" @@ -28,38 +26,16 @@ const ( var eventCCArgs = [][]byte{[]byte("invoke"), []byte("SEVERE")} func TestEvents(t *testing.T) { - testSetup := initializeTests(t) + chainCodeID := integration.GenerateRandomID() + testSetup := initializeTests(t, chainCodeID) testReconnectEventHub(t, testSetup) - testFailedTx(t, testSetup) - testFailedTxErrorCode(t, testSetup) - testMultipleBlockEventCallbacks(t, testSetup) + testFailedTx(t, testSetup, chainCodeID) + testFailedTxErrorCode(t, testSetup, chainCodeID) + testMultipleBlockEventCallbacks(t, testSetup, chainCodeID) } -func initializeTests(t *testing.T) integration.BaseSetupImpl { - testSetup := integration.BaseSetupImpl{ - ConfigFile: "../" + integration.ConfigTestFile, - - ChannelID: "mychannel", - OrgID: org1Name, - ChannelConfig: path.Join("../../", metadata.ChannelConfigPath, "mychannel.tx"), - ConnectEventHub: true, - } - - if err := testSetup.Initialize(t); err != nil { - t.Fatalf(err.Error()) - } - - testSetup.ChainCodeID = integration.GenerateRandomID() - - if err := testSetup.InstallAndInstantiateCC(testSetup.ChainCodeID, "github.com/events_cc", "v0", testSetup.GetDeployPath(), nil); err != nil { - t.Fatalf("InstallAndInstantiateCC return error: %v", err) - } - - return testSetup -} - -func testFailedTx(t *testing.T, testSetup integration.BaseSetupImpl) { +func testFailedTx(t *testing.T, testSetup integration.BaseSetupImpl, chainCodeID string) { fcn := "invoke" // Arguments for events CC @@ -67,21 +43,21 @@ func testFailedTx(t *testing.T, testSetup integration.BaseSetupImpl) { args = append(args, []byte("invoke")) args = append(args, []byte("SEVERE")) - tpResponses1, tx1, err := testSetup.CreateAndSendTransactionProposal(testSetup.Channel, testSetup.ChainCodeID, fcn, args, []apifabclient.ProposalProcessor{testSetup.Channel.PrimaryPeer()}, nil) + tpResponses1, tx1, err := integration.CreateAndSendTransactionProposal(testSetup.Channel, chainCodeID, fcn, args, []apifabclient.ProposalProcessor{testSetup.Channel.PrimaryPeer()}, nil) if err != nil { t.Fatalf("CreateAndSendTransactionProposal return error: %v", err) } - tpResponses2, tx2, err := testSetup.CreateAndSendTransactionProposal(testSetup.Channel, testSetup.ChainCodeID, fcn, args, []apifabclient.ProposalProcessor{testSetup.Channel.PrimaryPeer()}, nil) + tpResponses2, tx2, err := integration.CreateAndSendTransactionProposal(testSetup.Channel, chainCodeID, fcn, args, []apifabclient.ProposalProcessor{testSetup.Channel.PrimaryPeer()}, nil) if err != nil { t.Fatalf("CreateAndSendTransactionProposal return error: %v", err) } // Register tx1 and tx2 for commit/block event(s) - done1, fail1 := testSetup.RegisterTxEvent(t, tx1, testSetup.EventHub) + done1, fail1 := integration.RegisterTxEvent(t, tx1, testSetup.EventHub) defer testSetup.EventHub.UnregisterTxEvent(tx1) - done2, fail2 := testSetup.RegisterTxEvent(t, tx2, testSetup.EventHub) + done2, fail2 := integration.RegisterTxEvent(t, tx2, testSetup.EventHub) defer testSetup.EventHub.UnregisterTxEvent(tx2) // Setup monitoring of events @@ -94,11 +70,11 @@ func testFailedTx(t *testing.T, testSetup integration.BaseSetupImpl) { // Test invalid transaction: create 2 invoke requests in quick succession that modify // the same state variable which should cause one invoke to be invalid - _, err = testSetup.CreateAndSendTransaction(testSetup.Channel, tpResponses1) + _, err = integration.CreateAndSendTransaction(testSetup.Channel, tpResponses1) if err != nil { t.Fatalf("First invoke failed err: %v", err) } - _, err = testSetup.CreateAndSendTransaction(testSetup.Channel, tpResponses2) + _, err = integration.CreateAndSendTransaction(testSetup.Channel, tpResponses2) if err != nil { t.Fatalf("Second invoke failed err: %v", err) } @@ -133,16 +109,16 @@ Loop: } } -func testFailedTxErrorCode(t *testing.T, testSetup integration.BaseSetupImpl) { +func testFailedTxErrorCode(t *testing.T, testSetup integration.BaseSetupImpl, chainCodeID string) { fcn := "invoke" - tpResponses1, tx1, err := testSetup.CreateAndSendTransactionProposal(testSetup.Channel, testSetup.ChainCodeID, fcn, eventCCArgs, []apifabclient.ProposalProcessor{testSetup.Channel.PrimaryPeer()}, nil) + tpResponses1, tx1, err := integration.CreateAndSendTransactionProposal(testSetup.Channel, chainCodeID, fcn, eventCCArgs, []apifabclient.ProposalProcessor{testSetup.Channel.PrimaryPeer()}, nil) if err != nil { t.Fatalf("CreateAndSendTransactionProposal return error: %v", err) } - tpResponses2, tx2, err := testSetup.CreateAndSendTransactionProposal(testSetup.Channel, testSetup.ChainCodeID, fcn, eventCCArgs, []apifabclient.ProposalProcessor{testSetup.Channel.PrimaryPeer()}, nil) + tpResponses2, tx2, err := integration.CreateAndSendTransactionProposal(testSetup.Channel, chainCodeID, fcn, eventCCArgs, []apifabclient.ProposalProcessor{testSetup.Channel.PrimaryPeer()}, nil) if err != nil { t.Fatalf("CreateAndSendTransactionProposal return error: %v", err) } @@ -183,11 +159,11 @@ func testFailedTxErrorCode(t *testing.T, testSetup integration.BaseSetupImpl) { // Test invalid transaction: create 2 invoke requests in quick succession that modify // the same state variable which should cause one invoke to be invalid - _, err = testSetup.CreateAndSendTransaction(testSetup.Channel, tpResponses1) + _, err = integration.CreateAndSendTransaction(testSetup.Channel, tpResponses1) if err != nil { t.Fatalf("First invoke failed err: %v", err) } - _, err = testSetup.CreateAndSendTransaction(testSetup.Channel, tpResponses2) + _, err = integration.CreateAndSendTransaction(testSetup.Channel, tpResponses2) if err != nil { t.Fatalf("Second invoke failed err: %v", err) } @@ -240,7 +216,7 @@ func testReconnectEventHub(t *testing.T, testSetup integration.BaseSetupImpl) { } } -func testMultipleBlockEventCallbacks(t *testing.T, testSetup integration.BaseSetupImpl) { +func testMultipleBlockEventCallbacks(t *testing.T, testSetup integration.BaseSetupImpl, chainCodeID string) { fcn := "invoke" // Create and register test callback that will be invoked upon block event @@ -250,13 +226,13 @@ func testMultipleBlockEventCallbacks(t *testing.T, testSetup integration.BaseSet test <- true }) - tpResponses, tx, err := testSetup.CreateAndSendTransactionProposal(testSetup.Channel, testSetup.ChainCodeID, fcn, eventCCArgs, []apifabclient.ProposalProcessor{testSetup.Channel.PrimaryPeer()}, nil) + tpResponses, tx, err := integration.CreateAndSendTransactionProposal(testSetup.Channel, chainCodeID, fcn, eventCCArgs, []apifabclient.ProposalProcessor{testSetup.Channel.PrimaryPeer()}, nil) if err != nil { t.Fatalf("CreateAndSendTransactionProposal returned error: %v", err) } // Register tx for commit/block event(s) - done, fail := testSetup.RegisterTxEvent(t, tx, testSetup.EventHub) + done, fail := integration.RegisterTxEvent(t, tx, testSetup.EventHub) defer testSetup.EventHub.UnregisterTxEvent(tx) // Setup monitoring of events @@ -267,7 +243,7 @@ func testMultipleBlockEventCallbacks(t *testing.T, testSetup integration.BaseSet monitorMultipleBlockEventCallbacks(t, testSetup, done, fail, test) }() - _, err = testSetup.CreateAndSendTransaction(testSetup.Channel, tpResponses) + _, err = integration.CreateAndSendTransaction(testSetup.Channel, tpResponses) if err != nil { t.Fatalf("CreateAndSendTransaction failed with error: %v", err) } diff --git a/test/integration/fab/install_chaincode_test.go b/test/integration/fab/install_chaincode_test.go index f699a3390b..bd37315298 100644 --- a/test/integration/fab/install_chaincode_test.go +++ b/test/integration/fab/install_chaincode_test.go @@ -51,7 +51,7 @@ func testChaincodeInstallUsingChaincodePath(t *testing.T, testSetup *integration // Retrieve installed chaincodes client := testSetup.Client - ccPkg, err := packager.NewCCPackage(chainCodePath, testSetup.GetDeployPath()) + ccPkg, err := packager.NewCCPackage(chainCodePath, integration.GetDeployPath()) if err != nil { t.Fatalf("Failed to package chaincode") } @@ -90,7 +90,7 @@ func testChaincodeInstallUsingChaincodePackage(t *testing.T, testSetup *integrat chainCodeVersion := getRandomCCVersion() - ccPkg, err := packager.NewCCPackage(chainCodePath, testSetup.GetDeployPath()) + ccPkg, err := packager.NewCCPackage(chainCodePath, integration.GetDeployPath()) if err != nil { t.Fatalf("PackageCC return error: %s", err) } diff --git a/test/integration/fab/main_test.go b/test/integration/fab/main_test.go index cb4e2419c9..ea7b361bd0 100644 --- a/test/integration/fab/main_test.go +++ b/test/integration/fab/main_test.go @@ -8,10 +8,13 @@ package fab import ( "fmt" "os" + "path" "testing" config "github.com/hyperledger/fabric-sdk-go/api/apiconfig" + "github.com/hyperledger/fabric-sdk-go/pkg/fabsdk" "github.com/hyperledger/fabric-sdk-go/test/integration" + "github.com/hyperledger/fabric-sdk-go/test/metadata" ) var testFabricConfig config.Config @@ -42,3 +45,24 @@ func teardown() { // do any teadown activities here .. testFabricConfig = nil } + +func initializeTests(t *testing.T, chainCodeID string) integration.BaseSetupImpl { + testSetup := integration.BaseSetupImpl{ + ConfigFile: "../" + integration.ConfigTestFile, + + ChannelID: "mychannel", + OrgID: org1Name, + ChannelConfig: path.Join("../../", metadata.ChannelConfigPath, "mychannel.tx"), + ConnectEventHub: true, + } + + if err := testSetup.Initialize(t); err != nil { + t.Fatalf(err.Error()) + } + + if err := integration.InstallAndInstantiateCC(testSetup.SDK, fabsdk.WithUser("Admin"), testSetup.OrgID, chainCodeID, "github.com/events_cc", "v0", integration.GetDeployPath(), nil); err != nil { + t.Fatalf("InstallAndInstantiateCC return error: %v", err) + } + + return testSetup +} diff --git a/test/integration/fab/transient_data_test.go b/test/integration/fab/transient_data_test.go index dbc1727476..1eba11c255 100644 --- a/test/integration/fab/transient_data_test.go +++ b/test/integration/fab/transient_data_test.go @@ -11,6 +11,7 @@ import ( "testing" "github.com/hyperledger/fabric-sdk-go/api/apifabclient" + "github.com/hyperledger/fabric-sdk-go/pkg/fabsdk" "github.com/hyperledger/fabric-sdk-go/test/integration" "github.com/hyperledger/fabric-sdk-go/test/metadata" ) @@ -30,7 +31,8 @@ func TestTransient(t *testing.T) { t.Fatalf(err.Error()) } - if err := testSetup.InstallAndInstantiateExampleCC(); err != nil { + chaincodeID := integration.GenerateRandomID() + if err := integration.InstallAndInstantiateExampleCC(testSetup.SDK, fabsdk.WithUser("Admin"), testSetup.OrgID, chaincodeID); err != nil { t.Fatalf("InstallAndInstantiateExampleCC return error: %v", err) } @@ -40,7 +42,7 @@ func TestTransient(t *testing.T) { transientDataMap := make(map[string][]byte) transientDataMap["result"] = []byte(transientData) - transactionProposalResponse, _, err := testSetup.CreateAndSendTransactionProposal(testSetup.Channel, testSetup.ChainCodeID, fcn, integration.ExampleCCTxArgs(), []apifabclient.ProposalProcessor{testSetup.Channel.PrimaryPeer()}, transientDataMap) + transactionProposalResponse, _, err := integration.CreateAndSendTransactionProposal(testSetup.Channel, chaincodeID, fcn, integration.ExampleCCTxArgs(), []apifabclient.ProposalProcessor{testSetup.Channel.PrimaryPeer()}, transientDataMap) if err != nil { t.Fatalf("CreateAndSendTransactionProposal return error: %v", err) } @@ -56,7 +58,7 @@ func TestTransient(t *testing.T) { } //transient data null transientDataMap["result"] = []byte{} - transactionProposalResponse, _, err = testSetup.CreateAndSendTransactionProposal(testSetup.Channel, testSetup.ChainCodeID, fcn, integration.ExampleCCTxArgs(), []apifabclient.ProposalProcessor{testSetup.Channel.PrimaryPeer()}, transientDataMap) + transactionProposalResponse, _, err = integration.CreateAndSendTransactionProposal(testSetup.Channel, chaincodeID, fcn, integration.ExampleCCTxArgs(), []apifabclient.ProposalProcessor{testSetup.Channel.PrimaryPeer()}, transientDataMap) if err != nil { t.Fatalf("CreateAndSendTransactionProposal with empty transient data return an error: %v", err) } diff --git a/test/integration/sdk/channel_client_test.go b/test/integration/sdk/channel_client_test.go index 4156f322ab..92e00b977e 100644 --- a/test/integration/sdk/channel_client_test.go +++ b/test/integration/sdk/channel_client_test.go @@ -40,7 +40,8 @@ func TestChannelClient(t *testing.T) { t.Fatalf(err.Error()) } - if err := testSetup.InstallAndInstantiateExampleCC(); err != nil { + chainCodeID := integration.GenerateRandomID() + if err := integration.InstallAndInstantiateExampleCC(testSetup.SDK, fabsdk.WithUser("Admin"), testSetup.OrgID, chainCodeID); err != nil { t.Fatalf("InstallAndInstantiateExampleCC return error: %v", err) } @@ -56,7 +57,7 @@ func TestChannelClient(t *testing.T) { } // Synchronous query - testQuery("200", testSetup.ChainCodeID, chClient, t) + testQuery("200", chainCodeID, chClient, t) transientData := "Some data" transientDataMap := make(map[string][]byte) @@ -65,7 +66,7 @@ func TestChannelClient(t *testing.T) { // Synchronous transaction response, err := chClient.Execute( chclient.Request{ - ChaincodeID: testSetup.ChainCodeID, + ChaincodeID: chainCodeID, Fcn: "invoke", Args: integration.ExampleCCTxArgs(), TransientMap: transientDataMap, @@ -79,25 +80,25 @@ func TestChannelClient(t *testing.T) { } // Verify transaction using query - testQueryWithOpts("201", testSetup.ChainCodeID, chClient, t) + testQueryWithOpts("201", chainCodeID, chClient, t) // transaction - testTransaction(testSetup.ChainCodeID, chClient, t) + testTransaction(chainCodeID, chClient, t) // Verify transaction - testQuery("202", testSetup.ChainCodeID, chClient, t) + testQuery("202", chainCodeID, chClient, t) // Verify that filter error and commit error did not modify value - testQuery("202", testSetup.ChainCodeID, chClient, t) + testQuery("202", chainCodeID, chClient, t) // Test register and receive chaincode event - testChaincodeEvent(testSetup.ChainCodeID, chClient, t) + testChaincodeEvent(chainCodeID, chClient, t) // Verify transaction with chain code event completed - testQuery("203", testSetup.ChainCodeID, chClient, t) + testQuery("203", chainCodeID, chClient, t) // Test invocation of custom handler - testInvokeHandler(testSetup.ChainCodeID, chClient, t) + testInvokeHandler(chainCodeID, chClient, t) // Release channel client resources err = chClient.Close() diff --git a/test/integration/sdk/custom_cryptosuite_test.go b/test/integration/sdk/custom_cryptosuite_test.go index b5c4c8b1b3..86eb03c449 100644 --- a/test/integration/sdk/custom_cryptosuite_test.go +++ b/test/integration/sdk/custom_cryptosuite_test.go @@ -41,7 +41,8 @@ func TestEndToEndForCustomCryptoSuite(t *testing.T) { t.Fatalf(err.Error()) } - if err := testSetup.InstallAndInstantiateExampleCC(); err != nil { + chainCodeID := integration.GenerateRandomID() + if err := integration.InstallAndInstantiateExampleCC(testSetup.SDK, fabsdk.WithUser("Admin"), testSetup.OrgID, chainCodeID); err != nil { t.Fatalf("InstallAndInstantiateExampleCC return error: %v", err) } @@ -68,7 +69,7 @@ func TestEndToEndForCustomCryptoSuite(t *testing.T) { t.Fatalf("Failed to create new channel client: %s", err) } - response, err := chClient.Query(chclient.Request{ChaincodeID: testSetup.ChainCodeID, Fcn: "invoke", Args: integration.ExampleCCQueryArgs()}) + response, err := chClient.Query(chclient.Request{ChaincodeID: chainCodeID, Fcn: "invoke", Args: integration.ExampleCCQueryArgs()}) if err != nil { t.Fatalf("Failed to query funds: %s", err) } diff --git a/test/integration/sdk/sdk_provider_test.go b/test/integration/sdk/sdk_provider_test.go index 49c29a488c..891002dfeb 100644 --- a/test/integration/sdk/sdk_provider_test.go +++ b/test/integration/sdk/sdk_provider_test.go @@ -39,7 +39,8 @@ func TestDynamicSelection(t *testing.T) { t.Fatalf(err.Error()) } - if err := testSetup.InstallAndInstantiateExampleCC(); err != nil { + chainCodeID := integration.GenerateRandomID() + if err := integration.InstallAndInstantiateExampleCC(testSetup.SDK, fabsdk.WithUser("Admin"), testSetup.OrgID, chainCodeID); err != nil { t.Fatalf("InstallAndInstantiateExampleCC return error: %v", err) } @@ -63,20 +64,20 @@ func TestDynamicSelection(t *testing.T) { // Release all channel client resources defer chClient.Close() - response, err := chClient.Query(chclient.Request{ChaincodeID: testSetup.ChainCodeID, Fcn: "invoke", Args: integration.ExampleCCQueryArgs()}) + response, err := chClient.Query(chclient.Request{ChaincodeID: chainCodeID, Fcn: "invoke", Args: integration.ExampleCCQueryArgs()}) if err != nil { t.Fatalf("Failed to query funds: %s", err) } value := response.Payload // Move funds - response, err = chClient.Execute(chclient.Request{ChaincodeID: testSetup.ChainCodeID, Fcn: "invoke", Args: integration.ExampleCCTxArgs()}) + response, err = chClient.Execute(chclient.Request{ChaincodeID: chainCodeID, Fcn: "invoke", Args: integration.ExampleCCTxArgs()}) if err != nil { t.Fatalf("Failed to move funds: %s", err) } // Verify move funds transaction result - response, err = chClient.Query(chclient.Request{ChaincodeID: testSetup.ChainCodeID, Fcn: "invoke", Args: integration.ExampleCCQueryArgs()}) + response, err = chClient.Query(chclient.Request{ChaincodeID: chainCodeID, Fcn: "invoke", Args: integration.ExampleCCQueryArgs()}) if err != nil { t.Fatalf("Failed to query funds after transaction: %s", err) } diff --git a/test/integration/utils.go b/test/integration/utils.go index 814748b414..4cf6724e18 100644 --- a/test/integration/utils.go +++ b/test/integration/utils.go @@ -10,7 +10,11 @@ import ( "math/rand" "time" + "github.com/hyperledger/fabric-sdk-go/api/apiconfig" fab "github.com/hyperledger/fabric-sdk-go/api/apifabclient" + chmgmt "github.com/hyperledger/fabric-sdk-go/api/apitxn/chmgmtclient" + "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/peer" + "github.com/hyperledger/fabric-sdk-go/pkg/fabsdk" "github.com/pkg/errors" ) @@ -30,13 +34,114 @@ func randomString(strlen int) string { return string(result) } +// InitializeChannel ... +func InitializeChannel(sdk *fabsdk.FabricSDK, orgID string, req chmgmt.SaveChannelRequest, targets []fab.ProposalProcessor) error { + joinedTargets, err := FilterTargetsJoinedChannel(sdk, orgID, req.ChannelID, targets) + if err != nil { + return errors.WithMessage(err, "checking for joined targets failed") + } + + if len(joinedTargets) != len(targets) { + _, err := CreateChannel(sdk, req) + if err != nil { + return errors.Wrapf(err, "create channel failed") + } + + _, err = JoinChannel(sdk, req.ChannelID) + if err != nil { + return errors.Wrapf(err, "join channel failed") + } + } + return nil +} + +// FilterTargetsJoinedChannel filters targets to those that have joined the named channel. +func FilterTargetsJoinedChannel(sdk *fabsdk.FabricSDK, orgID string, channelID string, targets []fab.ProposalProcessor) ([]fab.ProposalProcessor, error) { + joinedTargets := []fab.ProposalProcessor{} + session, err := sdk.NewClient(fabsdk.WithUser("Admin"), fabsdk.WithOrg(orgID)).Session() + if err != nil { + return nil, errors.WithMessage(err, "failed getting admin user session for org") + } + + sc, err := sdk.FabricProvider().CreateResourceClient(session) + if err != nil { + return nil, errors.WithMessage(err, "NewResourceClient failed") + } + + for _, target := range targets { + // Check if primary peer has joined channel + alreadyJoined, err := HasPeerJoinedChannel(sc, target, channelID) + if err != nil { + return nil, errors.WithMessage(err, "failed while checking if primary peer has already joined channel") + } + if alreadyJoined { + joinedTargets = append(joinedTargets, target) + } + } + return joinedTargets, nil +} + +// CreateChannel attempts to save the named channel. +func CreateChannel(sdk *fabsdk.FabricSDK, req chmgmt.SaveChannelRequest) (bool, error) { + + // Channel management client is responsible for managing channels (create/update) + chMgmtClient, err := sdk.NewClient(fabsdk.WithUser("Admin"), fabsdk.WithOrg("ordererorg")).ChannelMgmt() + if err != nil { + return false, errors.WithMessage(err, "Failed to create new channel management client") + } + + // Create channel (or update if it already exists) + if err = chMgmtClient.SaveChannel(req); err != nil { + return false, nil + } + + time.Sleep(time.Second * 5) + return true, nil +} + +// JoinChannel attempts to save the named channel. +func JoinChannel(sdk *fabsdk.FabricSDK, name string) (bool, error) { + // Resource management client is responsible for managing resources (joining channels, install/instantiate/upgrade chaincodes) + resMgmtClient, err := sdk.NewClient(fabsdk.WithUser("Admin")).ResourceMgmt() + if err != nil { + return false, errors.WithMessage(err, "Failed to create new resource management client") + } + + if err = resMgmtClient.JoinChannel(name); err != nil { + return false, nil + } + return true, nil +} + +// CreateProposalProcessors initializes target peers based on config +func CreateProposalProcessors(config apiconfig.Config, orgs []string) ([]fab.ProposalProcessor, error) { + peers := []fab.ProposalProcessor{} + for _, org := range orgs { + peerConfig, err := config.PeersConfig(org) + if err != nil { + return nil, errors.WithMessage(err, "reading peer config failed") + } + for _, p := range peerConfig { + endorser, err := peer.New(config, peer.FromPeerConfig(&apiconfig.NetworkPeer{PeerConfig: p})) + if err != nil { + return nil, errors.WithMessage(err, "NewPeer failed") + } + peers = append(peers, endorser) + if err != nil { + return nil, errors.WithMessage(err, "adding peer failed") + } + } + } + return peers, nil +} + // HasPeerJoinedChannel checks whether the peer has already joined the channel. // It returns true if it has, false otherwise, or an error -func HasPeerJoinedChannel(client fab.Resource, peer fab.Peer, channel string) (bool, error) { +func HasPeerJoinedChannel(client fab.Resource, peer fab.ProposalProcessor, channel string) (bool, error) { foundChannel := false response, err := client.QueryChannels(peer) if err != nil { - return false, errors.WithMessage(err, "failed to query channel for primary peer") + return false, errors.WithMessage(err, "failed to query channel for peer") } for _, responseChannel := range response.Channels { if responseChannel.ChannelId == channel {