diff --git a/pkg/fabric-client/client.go b/pkg/fabric-client/client.go index 0850d369ba..a7642489af 100644 --- a/pkg/fabric-client/client.go +++ b/pkg/fabric-client/client.go @@ -8,10 +8,6 @@ package fabricclient import ( "encoding/json" - "time" - - "github.com/golang/protobuf/proto" - google_protobuf "github.com/golang/protobuf/ptypes/timestamp" config "github.com/hyperledger/fabric-sdk-go/api/apiconfig" fab "github.com/hyperledger/fabric-sdk-go/api/apifabclient" @@ -20,16 +16,11 @@ import ( pb "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/protos/peer" "github.com/hyperledger/fabric-sdk-go/api/apicryptosuite" - fcutils "github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric/common/util" - ccomm "github.com/hyperledger/fabric-sdk-go/pkg/config/comm" "github.com/hyperledger/fabric-sdk-go/pkg/errors" channel "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/channel" "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/identity" - "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/internal" - fc "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/internal" - "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/internal/txnproc" + "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/resource" "github.com/hyperledger/fabric-sdk-go/pkg/logging" - protos_utils "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/protos/utils" ) var logger = logging.NewLogger("fabric_sdk_go") @@ -45,6 +36,8 @@ type Client struct { } // NewClient returns a Client instance. +// +// Deprecated: see fabsdk package. func NewClient(config config.Config) *Client { channels := make(map[string]fab.Channel) c := Client{channels: channels, config: config} @@ -52,6 +45,8 @@ func NewClient(config config.Config) *Client { } // NewChannel returns a channel instance with the given name. +// +// Deprecated: see fabsdk package. func (c *Client) NewChannel(name string) (fab.Channel, error) { if _, ok := c.channels[name]; ok { return nil, errors.Errorf("channel %s already exists", name) @@ -89,6 +84,8 @@ func (c *Client) QueryChannelInfo(name string, peers []fab.Peer) (fab.Channel, e } // SetStateStore ... +// +// Deprecated: see fabsdk package. /* * The SDK should have a built-in key value store implementation (suggest a file-based implementation to allow easy setup during * development). But production systems would want a store backed by database for more robust storage and clustering, @@ -105,6 +102,8 @@ func (c *Client) StateStore() fab.KeyValueStore { } // SetCryptoSuite is a convenience method for obtaining the state store object in use for this client. +// +// Deprecated: see fabsdk package. func (c *Client) SetCryptoSuite(cryptoSuite apicryptosuite.CryptoSuite) { c.cryptoSuite = cryptoSuite } @@ -120,6 +119,8 @@ func (c *Client) SigningManager() fab.SigningManager { } // SetSigningManager is a convenience method to set signing manager +// +// Deprecated: see fabsdk package. func (c *Client) SetSigningManager(signingMgr fab.SigningManager) { c.signingManager = signingMgr } @@ -208,27 +209,8 @@ func (c *Client) LoadUserFromStateStore(name string) (fab.User, error) { * @returns {byte[]} The bytes of the ConfigUpdate protobuf */ func (c *Client) ExtractChannelConfig(configEnvelope []byte) ([]byte, error) { - logger.Debug("extractConfigUpdate - start") - - envelope := &common.Envelope{} - err := proto.Unmarshal(configEnvelope, envelope) - if err != nil { - return nil, errors.Wrap(err, "unmarshal config envelope failed") - } - - payload := &common.Payload{} - err = proto.Unmarshal(envelope.Payload, payload) - if err != nil { - return nil, errors.Wrap(err, "unmarshal envelope payload failed") - } - - configUpdateEnvelope := &common.ConfigUpdateEnvelope{} - err = proto.Unmarshal(payload.Data, configUpdateEnvelope) - if err != nil { - return nil, errors.Wrap(err, "unmarshal config update envelope") - } - - return configUpdateEnvelope.ConfigUpdate, nil + rc := resource.New(c) + return rc.ExtractChannelConfig(configEnvelope) } // SignChannelConfig ... @@ -238,59 +220,8 @@ func (c *Client) ExtractChannelConfig(configEnvelope []byte) ([]byte, error) { * @return {ConfigSignature} - The signature of the current user on the config bytes */ func (c *Client) SignChannelConfig(config []byte, signer fab.IdentityContext) (*common.ConfigSignature, error) { - logger.Debug("SignChannelConfig - start") - - if config == nil { - return nil, errors.New("channel configuration required") - } - - signingUser := signer - // If signing user is not provided default to client's user context - if signingUser == nil { - signingUser = c.signingIdentity - } - - if signingUser == nil { - return nil, errors.New("user context required") - } - - creator, err := signingUser.Identity() - if err != nil { - return nil, errors.WithMessage(err, "failed to get user context's identity") - } - nonce, err := fc.GenerateRandomNonce() - if err != nil { - return nil, errors.Wrap(err, "GenerateRandomNonce failed") - } - - // signature is across a signature header and the config update - signatureHeader := &common.SignatureHeader{ - Creator: creator, - Nonce: nonce, - } - signatureHeaderBytes, err := proto.Marshal(signatureHeader) - if err != nil { - return nil, errors.Wrap(err, "marshal signatureHeader failed") - } - - signingMgr := c.SigningManager() - if signingMgr == nil { - return nil, errors.New("signing manager is nil") - } - - // get all the bytes to be signed together, then sign - signingBytes := fcutils.ConcatenateBytes(signatureHeaderBytes, config) - signature, err := signingMgr.Sign(signingBytes, signingUser.PrivateKey()) - if err != nil { - return nil, errors.WithMessage(err, "signing of channel config failed") - } - - // build the return object - configSignature := &common.ConfigSignature{ - SignatureHeader: signatureHeaderBytes, - Signature: signature, - } - return configSignature, nil + rc := resource.New(c) + return rc.SignChannelConfig(config, signer) } // CreateChannel ... @@ -315,228 +246,27 @@ func (c *Client) SignChannelConfig(config []byte, signer fab.IdentityContext) (* * @returns {Result} Result Object with status on the create process. */ func (c *Client) CreateChannel(request fab.CreateChannelRequest) (apitxn.TransactionID, error) { - haveEnvelope := false - if request.Envelope != nil { - logger.Debug("createChannel - have envelope") - haveEnvelope = true - } - - if !haveEnvelope && request.TxnID.ID == "" { - txnID, err := c.newTxnID() - if err != nil { - return txnID, err - } - request.TxnID = txnID - } - - return request.TxnID, c.createOrUpdateChannel(request, haveEnvelope) -} - -// createOrUpdateChannel creates a new channel or updates an existing channel. -func (c *Client) createOrUpdateChannel(request fab.CreateChannelRequest, haveEnvelope bool) error { - // Validate request - if request.Config == nil && !haveEnvelope { - return errors.New("missing envelope request parameter containing the configuration of the new channel") - } - - if request.Signatures == nil && !haveEnvelope { - return errors.New("missing signatures request parameter for the new channel") - } - - if request.TxnID.ID == "" && !haveEnvelope { - return errors.New("txId required") - } - - if request.TxnID.Nonce == nil && !haveEnvelope { - return errors.New("nonce required") - } - - if request.Orderer == nil { - return errors.New("missing orderer request parameter for the initialize channel") - } - - if request.Name == "" { - return errors.New("missing name request parameter for the new channel") - } - - // channel = null; - var signature []byte - var payloadBytes []byte - - if haveEnvelope { - logger.Debug("createOrUpdateChannel - have envelope") - envelope := &common.Envelope{} - err := proto.Unmarshal(request.Envelope, envelope) - if err != nil { - return errors.Wrap(err, "unmarshal request envelope failed") - } - signature = envelope.Signature - payloadBytes = envelope.Payload - } else { - logger.Debug("createOrUpdateChannel - have config_update") - configUpdateEnvelope := &common.ConfigUpdateEnvelope{ - ConfigUpdate: request.Config, - Signatures: request.Signatures, - } - - // TODO: Move - tlsCertHash := ccomm.TLSCertHash(c.config) - channelHeader, err := channel.BuildChannelHeader(common.HeaderType_CONFIG_UPDATE, request.Name, request.TxnID.ID, 0, "", time.Now(), tlsCertHash) - if err != nil { - return errors.WithMessage(err, "BuildChannelHeader failed") - } - if c.signingIdentity == nil { - return errors.New("identity context is nil") - } - creator, err := c.signingIdentity.Identity() - if err != nil { - return errors.WithMessage(err, "getting creator failed") - } - - header, err := fc.BuildHeader(creator, channelHeader, request.TxnID.Nonce) - if err != nil { - return errors.Wrap(err, "BuildHeader failed") - } - configUpdateEnvelopeBytes, err := proto.Marshal(configUpdateEnvelope) - if err != nil { - return errors.Wrap(err, "marshal configUpdateEnvelope failed") - } - payload := &common.Payload{ - Header: header, - Data: configUpdateEnvelopeBytes, - } - payloadBytes, err = proto.Marshal(payload) - if err != nil { - return errors.Wrap(err, "marshal payload failed") - } - - signingMgr := c.SigningManager() - if signingMgr == nil { - return errors.New("signing manager is nil") - } - - signature, err = signingMgr.Sign(payloadBytes, c.signingIdentity.PrivateKey()) - if err != nil { - return errors.WithMessage(err, "signing payload failed") - } - } - - // Send request - _, err := request.Orderer.SendBroadcast(&fab.SignedEnvelope{ - Signature: signature, - Payload: payloadBytes, - }) - if err != nil { - return errors.WithMessage(err, "failed broadcast to orderer") - } - - return nil + rc := resource.New(c) + return rc.CreateChannel(request) } // QueryChannels queries the names of all the channels that a peer has joined. func (c *Client) QueryChannels(peer fab.Peer) (*pb.ChannelQueryResponse, error) { - - if peer == nil { - return nil, errors.New("peer required") - } - - payload, err := c.queryBySystemChaincodeByTarget("cscc", "GetChannels", [][]byte{}, peer) - if err != nil { - return nil, errors.WithMessage(err, "cscc.GetChannels failed") - } - - response := new(pb.ChannelQueryResponse) - err = proto.Unmarshal(payload, response) - if err != nil { - return nil, errors.Wrap(err, "unmarshal ChannelQueryResponse failed") - } - return response, nil + rc := resource.New(c) + return rc.QueryChannels(peer) } // QueryInstalledChaincodes queries the installed chaincodes on a peer. // Returns the details of all chaincodes installed on a peer. func (c *Client) QueryInstalledChaincodes(peer fab.Peer) (*pb.ChaincodeQueryResponse, error) { - - if peer == nil { - return nil, errors.New("peer required") - } - payload, err := c.queryBySystemChaincodeByTarget("lscc", "getinstalledchaincodes", [][]byte{}, peer) - if err != nil { - return nil, errors.WithMessage(err, "lscc.getinstalledchaincodes failed") - } - response := new(pb.ChaincodeQueryResponse) - err = proto.Unmarshal(payload, response) - if err != nil { - return nil, errors.Wrap(err, "unmarshal ChaincodeQueryResponse failed") - } - - return response, nil + rc := resource.New(c) + return rc.QueryInstalledChaincodes(peer) } // InstallChaincode sends an install proposal to one or more endorsing peers. func (c *Client) InstallChaincode(req fab.InstallChaincodeRequest) ([]*apitxn.TransactionProposalResponse, string, error) { - - if req.Name == "" { - return nil, "", errors.New("chaincode name required") - } - if req.Path == "" { - return nil, "", errors.New("chaincode path required") - } - if req.Version == "" { - return nil, "", errors.New("chaincode version required") - } - if req.Package == nil { - return nil, "", errors.New("chaincode package is required") - } - - now := time.Now() - cds := &pb.ChaincodeDeploymentSpec{ChaincodeSpec: &pb.ChaincodeSpec{ - Type: req.Package.Type, ChaincodeId: &pb.ChaincodeID{Name: req.Name, Path: req.Path, Version: req.Version}}, - CodePackage: req.Package.Code, EffectiveDate: &google_protobuf.Timestamp{Seconds: int64(now.Second()), Nanos: int32(now.Nanosecond())}} - - if c.signingIdentity == nil { - return nil, "", errors.New("signing identity required") - } - creator, err := c.signingIdentity.Identity() - if err != nil { - return nil, "", errors.Wrap(err, "failed to get creator identity") - } - - // create an install from a chaincodeDeploymentSpec - proposal, txID, err := protos_utils.CreateInstallProposalFromCDS(cds, creator) - if err != nil { - return nil, "", errors.Wrap(err, "failed to create chaincode deploy proposal") - } - proposalBytes, err := protos_utils.GetBytesProposal(proposal) - if err != nil { - return nil, "", err - } - user := c.signingIdentity - if user == nil { - return nil, "", errors.New("User context is nil") - } - - signingMgr := c.SigningManager() - if signingMgr == nil { - return nil, "", errors.Errorf("signing manager is nil") - } - - signature, err := signingMgr.Sign(proposalBytes, user.PrivateKey()) - if err != nil { - return nil, "", err - } - - signedProposal := &pb.SignedProposal{ProposalBytes: proposalBytes, Signature: signature} - - txnID := apitxn.TransactionID{ID: txID} // Nonce is missing - - transactionProposalResponse, err := txnproc.SendTransactionProposalToProcessors(&apitxn.TransactionProposal{ - SignedProposal: signedProposal, - Proposal: proposal, - TxnID: txnID, - }, req.Targets) - - return transactionProposalResponse, txID, err + rc := resource.New(c) + return rc.InstallChaincode(req) } // IdentityContext returns the current identity for signing. @@ -548,34 +278,3 @@ func (c *Client) IdentityContext() fab.IdentityContext { func (c *Client) SetIdentityContext(user fab.IdentityContext) { c.signingIdentity = user } - -// newTxnID computes a TransactionID for the current user context -func (c *Client) newTxnID() (apitxn.TransactionID, error) { - if c.signingIdentity == nil { - return apitxn.TransactionID{}, errors.New("user context is nil") - } - - return internal.NewTxnID(c.signingIdentity) -} - -func (c *Client) queryBySystemChaincodeByTarget(chaincodeID string, fcn string, args [][]byte, target apitxn.ProposalProcessor) ([]byte, error) { - targets := []apitxn.ProposalProcessor{target} - request := apitxn.ChaincodeInvokeRequest{ - ChaincodeID: chaincodeID, - Fcn: fcn, - Args: args, - Targets: targets, - } - responses, err := channel.QueryBySystemChaincode(request, c) - - if err != nil { - return nil, errors.WithMessage(err, "QueryBySystemChaincode failed") - } - // we are only querying one peer hence one result - if len(responses) != 1 { - return nil, errors.Errorf("QueryBySystemChaincode should have one result only - actual result count: %d", len(responses)) - } - - return responses[0], nil - -} diff --git a/pkg/fabric-client/client_test.go b/pkg/fabric-client/client_test.go index 5a6b1c27ce..2cd95cc228 100644 --- a/pkg/fabric-client/client_test.go +++ b/pkg/fabric-client/client_test.go @@ -8,14 +8,9 @@ package fabricclient import ( "bytes" - "fmt" - "io/ioutil" - "path" "testing" - "time" fab "github.com/hyperledger/fabric-sdk-go/api/apifabclient" - "github.com/hyperledger/fabric-sdk-go/test/metadata" "github.com/hyperledger/fabric-sdk-go/pkg/cryptosuite/bccsp/sw" kvs "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/keyvaluestore" @@ -118,63 +113,6 @@ func TestClientMethods(t *testing.T) { } } -func TestCreateChannel(t *testing.T) { - client := NewClient(mocks.NewMockConfig()) - - configTx, err := ioutil.ReadFile(path.Join("../../", metadata.ChannelConfigPath, "mychannel.tx")) - if err != nil { - t.Fatalf(err.Error()) - } - - // Setup mock orderer - verifyBroadcast := make(chan *fab.SignedEnvelope) - orderer := mocks.NewMockOrderer(fmt.Sprintf("0.0.0.0:1234"), verifyBroadcast) - - // Create channel without envelope - _, err = client.CreateChannel(fab.CreateChannelRequest{ - Orderer: orderer, - Name: "mychannel", - }) - if err == nil { - t.Fatalf("Expected error creating channel without envelope") - } - - // Create channel without orderer - _, err = client.CreateChannel(fab.CreateChannelRequest{ - Envelope: configTx, - Name: "mychannel", - }) - if err == nil { - t.Fatalf("Expected error creating channel without orderer") - } - - // Create channel without name - _, err = client.CreateChannel(fab.CreateChannelRequest{ - Envelope: configTx, - Orderer: orderer, - }) - if err == nil { - t.Fatalf("Expected error creating channel without name") - } - - // Test with valid cofiguration - request := fab.CreateChannelRequest{ - Envelope: configTx, - Orderer: orderer, - Name: "mychannel", - } - _, err = client.CreateChannel(request) - if err != nil { - t.Fatalf("Did not expect error from create channel. Got error: %v", err) - } - select { - case b := <-verifyBroadcast: - logger.Debugf("Verified broadcast: %v", b) - case <-time.After(time.Second): - t.Fatalf("Expected broadcast") - } -} - func TestQueryMethodsOnClient(t *testing.T) { client := NewClient(mocks.NewMockConfig()) diff --git a/pkg/fabric-client/resource/resource.go b/pkg/fabric-client/resource/resource.go new file mode 100644 index 0000000000..bfb2c9cf93 --- /dev/null +++ b/pkg/fabric-client/resource/resource.go @@ -0,0 +1,382 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +// Package resource provides access to fabric network resource management, typically using system channel queries. +package resource + +import ( + "time" + + "github.com/golang/protobuf/proto" + google_protobuf "github.com/golang/protobuf/ptypes/timestamp" + + fab "github.com/hyperledger/fabric-sdk-go/api/apifabclient" + "github.com/hyperledger/fabric-sdk-go/api/apitxn" + + fcutils "github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric/common/util" + ccomm "github.com/hyperledger/fabric-sdk-go/pkg/config/comm" + "github.com/hyperledger/fabric-sdk-go/pkg/errors" + "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/channel" + "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/internal" + fc "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/internal" + "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/internal/txnproc" + "github.com/hyperledger/fabric-sdk-go/pkg/logging" + "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" + protos_utils "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/protos/utils" +) + +var logger = logging.NewLogger("fabric_sdk_go") + +// Resource is a client that provides access to fabric network resource management. +type Resource struct { + clientContext fab.Context +} + +// New returns a Client instance with the SDK context. +func New(ctx fab.Context) *Resource { + c := Resource{clientContext: ctx} + return &c +} + +// ExtractChannelConfig extracts the protobuf 'ConfigUpdate' object out of the 'ConfigEnvelope'. +func (c *Resource) ExtractChannelConfig(configEnvelope []byte) ([]byte, error) { + logger.Debug("extractConfigUpdate - start") + + envelope := &common.Envelope{} + err := proto.Unmarshal(configEnvelope, envelope) + if err != nil { + return nil, errors.Wrap(err, "unmarshal config envelope failed") + } + + payload := &common.Payload{} + err = proto.Unmarshal(envelope.Payload, payload) + if err != nil { + return nil, errors.Wrap(err, "unmarshal envelope payload failed") + } + + configUpdateEnvelope := &common.ConfigUpdateEnvelope{} + err = proto.Unmarshal(payload.Data, configUpdateEnvelope) + if err != nil { + return nil, errors.Wrap(err, "unmarshal config update envelope") + } + + return configUpdateEnvelope.ConfigUpdate, nil +} + +// SignChannelConfig signs a configuration. +func (c *Resource) SignChannelConfig(config []byte, signer fab.IdentityContext) (*common.ConfigSignature, error) { + logger.Debug("SignChannelConfig - start") + + if config == nil { + return nil, errors.New("channel configuration required") + } + + signingUser := signer + // If signing user is not provided default to client's user context + if signingUser == nil { + signingUser = c.clientContext.IdentityContext() + } + + if signingUser == nil { + return nil, errors.New("user context required") + } + + creator, err := signingUser.Identity() + if err != nil { + return nil, errors.WithMessage(err, "failed to get user context's identity") + } + nonce, err := fc.GenerateRandomNonce() + if err != nil { + return nil, errors.Wrap(err, "GenerateRandomNonce failed") + } + + // signature is across a signature header and the config update + signatureHeader := &common.SignatureHeader{ + Creator: creator, + Nonce: nonce, + } + signatureHeaderBytes, err := proto.Marshal(signatureHeader) + if err != nil { + return nil, errors.Wrap(err, "marshal signatureHeader failed") + } + + signingMgr := c.clientContext.SigningManager() + if signingMgr == nil { + return nil, errors.New("signing manager is nil") + } + + // get all the bytes to be signed together, then sign + signingBytes := fcutils.ConcatenateBytes(signatureHeaderBytes, config) + signature, err := signingMgr.Sign(signingBytes, signingUser.PrivateKey()) + if err != nil { + return nil, errors.WithMessage(err, "signing of channel config failed") + } + + // build the return object + configSignature := &common.ConfigSignature{ + SignatureHeader: signatureHeaderBytes, + Signature: signature, + } + return configSignature, nil +} + +// CreateChannel calls the orderer to start building the new channel. +func (c *Resource) CreateChannel(request fab.CreateChannelRequest) (apitxn.TransactionID, error) { + haveEnvelope := false + if request.Envelope != nil { + logger.Debug("createChannel - have envelope") + haveEnvelope = true + } + + if !haveEnvelope && request.TxnID.ID == "" { + txnID, err := c.newTxnID() + if err != nil { + return txnID, err + } + request.TxnID = txnID + } + + return request.TxnID, c.createOrUpdateChannel(request, haveEnvelope) +} + +// createOrUpdateChannel creates a new channel or updates an existing channel. +func (c *Resource) createOrUpdateChannel(request fab.CreateChannelRequest, haveEnvelope bool) error { + // Validate request + if request.Config == nil && !haveEnvelope { + return errors.New("missing envelope request parameter containing the configuration of the new channel") + } + + if request.Signatures == nil && !haveEnvelope { + return errors.New("missing signatures request parameter for the new channel") + } + + if request.TxnID.ID == "" && !haveEnvelope { + return errors.New("txId required") + } + + if request.TxnID.Nonce == nil && !haveEnvelope { + return errors.New("nonce required") + } + + if request.Orderer == nil { + return errors.New("missing orderer request parameter for the initialize channel") + } + + if request.Name == "" { + return errors.New("missing name request parameter for the new channel") + } + + // channel = null; + var signature []byte + var payloadBytes []byte + + if haveEnvelope { + logger.Debug("createOrUpdateChannel - have envelope") + envelope := &common.Envelope{} + err := proto.Unmarshal(request.Envelope, envelope) + if err != nil { + return errors.Wrap(err, "unmarshal request envelope failed") + } + signature = envelope.Signature + payloadBytes = envelope.Payload + } else { + logger.Debug("createOrUpdateChannel - have config_update") + configUpdateEnvelope := &common.ConfigUpdateEnvelope{ + ConfigUpdate: request.Config, + Signatures: request.Signatures, + } + + // TODO: Move + tlsCertHash := ccomm.TLSCertHash(c.clientContext.Config()) + channelHeader, err := channel.BuildChannelHeader(common.HeaderType_CONFIG_UPDATE, request.Name, request.TxnID.ID, 0, "", time.Now(), tlsCertHash) + if err != nil { + return errors.WithMessage(err, "BuildChannelHeader failed") + } + if c.clientContext.IdentityContext() == nil { + return errors.New("identity context is nil") + } + creator, err := c.clientContext.IdentityContext().Identity() + if err != nil { + return errors.WithMessage(err, "getting creator failed") + } + + header, err := fc.BuildHeader(creator, channelHeader, request.TxnID.Nonce) + if err != nil { + return errors.Wrap(err, "BuildHeader failed") + } + configUpdateEnvelopeBytes, err := proto.Marshal(configUpdateEnvelope) + if err != nil { + return errors.Wrap(err, "marshal configUpdateEnvelope failed") + } + payload := &common.Payload{ + Header: header, + Data: configUpdateEnvelopeBytes, + } + payloadBytes, err = proto.Marshal(payload) + if err != nil { + return errors.Wrap(err, "marshal payload failed") + } + + signingMgr := c.clientContext.SigningManager() + if signingMgr == nil { + return errors.New("signing manager is nil") + } + + signature, err = signingMgr.Sign(payloadBytes, c.clientContext.IdentityContext().PrivateKey()) + if err != nil { + return errors.WithMessage(err, "signing payload failed") + } + } + + // Send request + _, err := request.Orderer.SendBroadcast(&fab.SignedEnvelope{ + Signature: signature, + Payload: payloadBytes, + }) + if err != nil { + return errors.WithMessage(err, "failed broadcast to orderer") + } + + return nil +} + +// QueryChannels queries the names of all the channels that a peer has joined. +func (c *Resource) QueryChannels(peer fab.Peer) (*pb.ChannelQueryResponse, error) { + + if peer == nil { + return nil, errors.New("peer required") + } + + payload, err := c.queryBySystemChaincodeByTarget("cscc", "GetChannels", [][]byte{}, peer) + if err != nil { + return nil, errors.WithMessage(err, "cscc.GetChannels failed") + } + + response := new(pb.ChannelQueryResponse) + err = proto.Unmarshal(payload, response) + if err != nil { + return nil, errors.Wrap(err, "unmarshal ChannelQueryResponse failed") + } + return response, nil +} + +// 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) { + + if peer == nil { + return nil, errors.New("peer required") + } + payload, err := c.queryBySystemChaincodeByTarget("lscc", "getinstalledchaincodes", [][]byte{}, peer) + if err != nil { + return nil, errors.WithMessage(err, "lscc.getinstalledchaincodes failed") + } + response := new(pb.ChaincodeQueryResponse) + err = proto.Unmarshal(payload, response) + if err != nil { + return nil, errors.Wrap(err, "unmarshal ChaincodeQueryResponse failed") + } + + return response, nil +} + +// InstallChaincode sends an install proposal to one or more endorsing peers. +func (c *Resource) InstallChaincode(req fab.InstallChaincodeRequest) ([]*apitxn.TransactionProposalResponse, string, error) { + + if req.Name == "" { + return nil, "", errors.New("chaincode name required") + } + if req.Path == "" { + return nil, "", errors.New("chaincode path required") + } + if req.Version == "" { + return nil, "", errors.New("chaincode version required") + } + if req.Package == nil { + return nil, "", errors.New("chaincode package is required") + } + + now := time.Now() + cds := &pb.ChaincodeDeploymentSpec{ChaincodeSpec: &pb.ChaincodeSpec{ + Type: req.Package.Type, ChaincodeId: &pb.ChaincodeID{Name: req.Name, Path: req.Path, Version: req.Version}}, + CodePackage: req.Package.Code, EffectiveDate: &google_protobuf.Timestamp{Seconds: int64(now.Second()), Nanos: int32(now.Nanosecond())}} + + if c.clientContext.IdentityContext() == nil { + return nil, "", errors.New("signing identity required") + } + creator, err := c.clientContext.IdentityContext().Identity() + if err != nil { + return nil, "", errors.Wrap(err, "failed to get creator identity") + } + + // create an install from a chaincodeDeploymentSpec + proposal, txID, err := protos_utils.CreateInstallProposalFromCDS(cds, creator) + if err != nil { + return nil, "", errors.Wrap(err, "failed to create chaincode deploy proposal") + } + proposalBytes, err := protos_utils.GetBytesProposal(proposal) + if err != nil { + return nil, "", err + } + user := c.clientContext.IdentityContext() + if user == nil { + return nil, "", errors.New("User context is nil") + } + + signingMgr := c.clientContext.SigningManager() + if signingMgr == nil { + return nil, "", errors.Errorf("signing manager is nil") + } + + signature, err := signingMgr.Sign(proposalBytes, user.PrivateKey()) + if err != nil { + return nil, "", err + } + + signedProposal := &pb.SignedProposal{ProposalBytes: proposalBytes, Signature: signature} + + txnID := apitxn.TransactionID{ID: txID} // Nonce is missing + + transactionProposalResponse, err := txnproc.SendTransactionProposalToProcessors(&apitxn.TransactionProposal{ + SignedProposal: signedProposal, + Proposal: proposal, + TxnID: txnID, + }, req.Targets) + + return transactionProposalResponse, txID, err +} + +// newTxnID computes a TransactionID for the current user context +func (c *Resource) newTxnID() (apitxn.TransactionID, error) { + if c.clientContext.IdentityContext() == nil { + return apitxn.TransactionID{}, errors.New("user context is nil") + } + + return internal.NewTxnID(c.clientContext.IdentityContext()) +} + +func (c *Resource) queryBySystemChaincodeByTarget(chaincodeID string, fcn string, args [][]byte, target apitxn.ProposalProcessor) ([]byte, error) { + targets := []apitxn.ProposalProcessor{target} + request := apitxn.ChaincodeInvokeRequest{ + ChaincodeID: chaincodeID, + Fcn: fcn, + Args: args, + Targets: targets, + } + responses, err := channel.QueryBySystemChaincode(request, c.clientContext) + + if err != nil { + return nil, errors.WithMessage(err, "QueryBySystemChaincode failed") + } + // we are only querying one peer hence one result + if len(responses) != 1 { + return nil, errors.Errorf("QueryBySystemChaincode should have one result only - actual result count: %d", len(responses)) + } + + return responses[0], nil + +} diff --git a/pkg/fabric-client/resource/resource_test.go b/pkg/fabric-client/resource/resource_test.go new file mode 100644 index 0000000000..f7fef541bb --- /dev/null +++ b/pkg/fabric-client/resource/resource_test.go @@ -0,0 +1,85 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package resource + +import ( + "fmt" + "io/ioutil" + "path" + "testing" + "time" + + fab "github.com/hyperledger/fabric-sdk-go/api/apifabclient" + "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/mocks" + "github.com/hyperledger/fabric-sdk-go/test/metadata" +) + +func TestCreateChannel(t *testing.T) { + client := setupTestClient() + + configTx, err := ioutil.ReadFile(path.Join("../../../", metadata.ChannelConfigPath, "mychannel.tx")) + if err != nil { + t.Fatalf(err.Error()) + } + + // Setup mock orderer + verifyBroadcast := make(chan *fab.SignedEnvelope) + orderer := mocks.NewMockOrderer(fmt.Sprintf("0.0.0.0:1234"), verifyBroadcast) + + // Create channel without envelope + _, err = client.CreateChannel(fab.CreateChannelRequest{ + Orderer: orderer, + Name: "mychannel", + }) + if err == nil { + t.Fatalf("Expected error creating channel without envelope") + } + + // Create channel without orderer + _, err = client.CreateChannel(fab.CreateChannelRequest{ + Envelope: configTx, + Name: "mychannel", + }) + if err == nil { + t.Fatalf("Expected error creating channel without orderer") + } + + // Create channel without name + _, err = client.CreateChannel(fab.CreateChannelRequest{ + Envelope: configTx, + Orderer: orderer, + }) + if err == nil { + t.Fatalf("Expected error creating channel without name") + } + + // Test with valid cofiguration + request := fab.CreateChannelRequest{ + Envelope: configTx, + Orderer: orderer, + Name: "mychannel", + } + _, err = client.CreateChannel(request) + if err != nil { + t.Fatalf("Did not expect error from create channel. Got error: %v", err) + } + select { + case b := <-verifyBroadcast: + logger.Debugf("Verified broadcast: %v", b) + case <-time.After(time.Second): + t.Fatalf("Expected broadcast") + } +} + +func setupTestClient() *Resource { + client := mocks.NewMockClient() + user := mocks.NewMockUser("test") + cryptoSuite := &mocks.MockCryptoSuite{} + client.SetIdentityContext(user) + client.SetCryptoSuite(cryptoSuite) + return New(client) +}