@@ -27,6 +27,7 @@ import (
2727 "time"
2828
2929 "github.com/golang/protobuf/proto"
30+ "github.com/hyperledger/fabric-sdk-go/fabric-client/util"
3031 "github.com/hyperledger/fabric/bccsp"
3132 msp "github.com/hyperledger/fabric/msp"
3233 "github.com/hyperledger/fabric/protos/common"
@@ -63,7 +64,8 @@ type Chain interface {
6364 AddOrderer (orderer Orderer )
6465 RemoveOrderer (orderer Orderer )
6566 GetOrderers () []Orderer
66- CreateChannel (request CreateChannelRequest ) error
67+ CreateChannel (request * CreateChannelRequest ) error
68+ JoinChannel (request * JoinChannelRequest ) error
6769 UpdateChain () bool
6870 IsReadonly () bool
6971 QueryInfo () (* common.BlockchainInfo , error )
@@ -174,6 +176,13 @@ type CreateChannelRequest struct {
174176 ConfigData []byte
175177}
176178
179+ // JoinChannelRequest allows a set of peers to transact on a channel on the network
180+ type JoinChannelRequest struct {
181+ Targets []Peer
182+ TxID string
183+ Nonce []byte
184+ }
185+
177186// NewChain ...
178187/**
179188 * @param {string} name to identify different chain instances. The naming of chain instances
@@ -388,10 +397,10 @@ func (c *chain) GetOrderers() []Orderer {
388397// CreateChannel calls the an orderer to create a channel on the network
389398// @param {CreateChannelRequest} request Contains cofiguration information
390399// @returns {bool} result of the channel creation
391- func (c * chain ) CreateChannel (request CreateChannelRequest ) error {
400+ func (c * chain ) CreateChannel (request * CreateChannelRequest ) error {
392401 var failureCount int
393402 // Validate request
394- if request .ConfigData == nil {
403+ if request == nil || request .ConfigData == nil {
395404 return fmt .Errorf ("Configuration is required to create a chanel" )
396405 }
397406
@@ -426,6 +435,89 @@ func (c *chain) CreateChannel(request CreateChannelRequest) error {
426435 return nil
427436}
428437
438+ // JoinChannel instructs a set of peers to join the channel represented by
439+ // this chain
440+ // @param {JoinChannelRequest} Join channel request
441+ // @returns error, if applicable
442+ func (c * chain ) JoinChannel (request * JoinChannelRequest ) error {
443+ joinCommand := "JoinChain"
444+ err := validateJoinChannelRequest (request )
445+ if err != nil {
446+ return err
447+ }
448+ // Fetch genesis block
449+ block , err := c .fetchGenesisBlock ()
450+ if err != nil {
451+ return err
452+ }
453+ blockBytes , err := proto .Marshal (block )
454+ if err != nil {
455+ return fmt .Errorf ("Error unmarshalling block: %s" , err )
456+ }
457+ // Get user enrolment info and serialize for signing requests
458+ user , err := c .clientContext .GetUserContext ("" )
459+ if err != nil {
460+ return fmt .Errorf ("GetUserContext returned error: %s" , err )
461+ }
462+ creatorID , err := getSerializedIdentity (user .GetEnrollmentCertificate ())
463+ if err != nil {
464+ return err
465+ }
466+ // Create join channel transaction proposal for target peers
467+ var args [][]byte
468+ args = append (args , []byte (joinCommand ))
469+ args = append (args , blockBytes )
470+ ccis := & pb.ChaincodeInvocationSpec {ChaincodeSpec : & pb.ChaincodeSpec {
471+ Type : pb .ChaincodeSpec_GOLANG , ChaincodeId : & pb.ChaincodeID {Name : "cscc" },
472+ Input : & pb.ChaincodeInput {Args : args }}}
473+
474+ // create a proposal from a ChaincodeInvocationSpec
475+ proposal , _ , err := protos_utils .
476+ CreateChaincodeProposalWithTxIDNonceAndTransient (request .TxID ,
477+ common .HeaderType_ENDORSER_TRANSACTION , "" , ccis ,
478+ request .Nonce , creatorID , nil )
479+ if err != nil {
480+ return fmt .Errorf ("Could not create chaincode proposal, err %s" , err )
481+ }
482+ // Serialize join proposal
483+ proposalBytes , err := protos_utils .GetBytesProposal (proposal )
484+ if err != nil {
485+ return err
486+ }
487+ // Sign join proposal
488+ signature , err := c .signObjectWithKey (proposalBytes , user .GetPrivateKey (),
489+ & bccsp.SHAOpts {}, nil )
490+ if err != nil {
491+ return err
492+ }
493+ // Send join proposal
494+ proposalResponses , err := c .SendTransactionProposal (& TransactionProposal {
495+ TransactionID : request .TxID ,
496+ signedProposal : & pb.SignedProposal {
497+ ProposalBytes : proposalBytes ,
498+ Signature : signature },
499+ proposal : proposal ,
500+ }, 0 , request .Targets )
501+ if err != nil {
502+ return fmt .Errorf ("Error sending join transaction proposal: %s" , err )
503+ }
504+
505+ // Check responses from target peers for success/failure
506+ var joinError string
507+ for _ , response := range proposalResponses {
508+ if response .Err != nil {
509+ joinError = joinError +
510+ fmt .Sprintf ("Join channel proposal response error: %s \n " ,
511+ response .Err .Error ())
512+ }
513+ }
514+ if joinError != "" {
515+ return fmt .Errorf (joinError )
516+ }
517+
518+ return nil
519+ }
520+
429521// UpdateChain ...
430522/**
431523 * Calls the orderer(s) to update an existing chain. This allows the addition and
@@ -1204,6 +1296,38 @@ func (c *chain) signProposal(proposal *pb.Proposal) (*pb.SignedProposal, error)
12041296 return & pb.SignedProposal {ProposalBytes : proposalBytes , Signature : signature }, nil
12051297}
12061298
1299+ // fetchGenesisBlock fetches the configuration block for this channel
1300+ func (c * chain ) fetchGenesisBlock () (* common.Block , error ) {
1301+ // Get user enrolment info and serialize for signing requests
1302+ user , err := c .clientContext .GetUserContext ("" )
1303+ if err != nil {
1304+ return nil , fmt .Errorf ("GetUserContext returned error: %s" , err )
1305+ }
1306+ creatorID , err := getSerializedIdentity (user .GetEnrollmentCertificate ())
1307+ if err != nil {
1308+ return nil , err
1309+ }
1310+ // Seek block zero (the configuration tx for this channel)
1311+ payload := util .CreateGenesisBlockRequest (c .name , creatorID )
1312+ blockRequest , err := c .SignPayload (payload )
1313+ if err != nil {
1314+ return nil , fmt .Errorf ("Error signing payload: %s" , err )
1315+ }
1316+ // Request genesis block from ordering service
1317+ var block * common.Block
1318+ // TODO: what if the primary orderer is down?
1319+ responses , errors := c .GetOrderers ()[0 ].SendDeliver (blockRequest )
1320+ // Block on channels for genesis block or error
1321+ select {
1322+ case block = <- responses :
1323+ logger .Debugf ("Got genesis block from ordering service: %#v" , block )
1324+ case err = <- errors :
1325+ return nil , fmt .Errorf ("Error from SendDeliver(): %s" , err )
1326+ }
1327+
1328+ return block , nil
1329+ }
1330+
12071331func getSerializedIdentity (userCertificate []byte ) ([]byte , error ) {
12081332 serializedIdentity := & msp.SerializedIdentity {Mspid : config .GetFabricCAID (),
12091333 IdBytes : userCertificate }
@@ -1242,3 +1366,19 @@ func buildChaincodePolicy(mspid string) (*common.SignaturePolicyEnvelope, error)
12421366 }
12431367 return p , nil
12441368}
1369+ func validateJoinChannelRequest (request * JoinChannelRequest ) error {
1370+ // Validate arguments
1371+ if request == nil {
1372+ return fmt .Errorf ("JoinChannelRequest argument is required to join channel" )
1373+ }
1374+ if request .Targets == nil || len (request .Targets ) == 0 {
1375+ return fmt .Errorf ("Atleast one target peer is required to join channel" )
1376+ }
1377+ if request .TxID == "" {
1378+ return fmt .Errorf ("Transaction ID is required to join channel" )
1379+ }
1380+ if request .Nonce == nil {
1381+ return fmt .Errorf ("Nonce is required to join channel" )
1382+ }
1383+ return nil
1384+ }
0 commit comments