'use strict' ; import { Gateway , GatewayOptions , Network , Wallet } from 'fabric-network' ; import { URL } from 'url' ; import { ConsoleOutputAdapter , FabricSmartContractDefinition , OutputAdapter } from 'ibm-blockchain-platform-common' ; import { FabricWallet } from 'ibm-blockchain-platform-wallet' ; import { Lifecycle , LifecyclePeer , LifecycleChannel , DefinedSmartContract } from 'ibm-blockchain-platform-fabric-admin' ; import { Endorser , Channel } from 'fabric-common' ; import { EvaluateQueryHandler } from 'ibm-blockchain-platform-fabric-admin' ; export abstract class FabricConnection { public identityName : string ; protected connectionProfilePath : string ; protected outputAdapter : OutputAdapter ; protected gateway : Gateway = new Gateway ( ) ; protected lifecycle : Lifecycle = new Lifecycle ( ) ; private discoveryAsLocalhost : boolean ; private discoveryEnabled : boolean ; private knownChannels : any ; constructor ( connectionProfilePath , outputAdapter ? ) { this . gateway = new Gateway ( ) ; this . connectionProfilePath = connectionProfilePath ; if ( ! outputAdapter ) { this . outputAdapter = ConsoleOutputAdapter . instance ( ) ; } else { this . outputAdapter = outputAdapter ; } } public abstract async connect ( wallet , identityName , timeout ) ; public getAllPeerNames ( ) { const allPeers = this . getAllPeers ( ) ; const peerNames = [ ] ; allPeers . forEach ( ( peer ) => { peerNames . push ( peer . name ) ; } ) ; return peerNames ; } public async getAllChannelsForPeer ( peerName ) { const peer = this . lifecycle . getPeer ( peerName , this . gateway . getOptions ( ) . wallet , this . gateway . getOptions ( ) . identity as string ) ; try { const channelNames = await peer . getAllChannelNames ( ) ; return channelNames . sort ( ) ; } catch ( error ) { if ( error . message && error . message . match ( /access denied/ ) ) { const channelNames = [ ] ; for ( const channelName in this . knownChannels ) { const channel = this . knownChannels [ channelName ] ; const peers = channel . peers || { } ; const peerNames = Object . keys ( peers ) ; if ( peerNames . indexOf ( peerName ) > - 1 ) { channelNames . push ( channelName ) ; } } if ( channelNames . length > 0 ) { return channelNames ; } } throw error ; } } public async getInstantiatedChaincode ( channelName ) { const lifecycleChannel = this . lifecycle . getChannel ( channelName , this . gateway . getOptions ( ) . wallet , this . gateway . getOptions ( ) . identity as string ) ; const channelMap = await this . createChannelMap ( ) ; const peerNames = channelMap . get ( channelName ) ; const peer = this . lifecycle . getPeer ( peerNames [ 0 ] , this . gateway . getOptions ( ) . wallet , this . gateway . getOptions ( ) . identity as string ) ; const capabilities = await peer . getChannelCapabilities ( channelName ) ; let smartContracts ; if ( ! capabilities . includes ( 'V2_0' ) ) { smartContracts = await lifecycleChannel . getAllInstantiatedSmartContracts ( peerNames [ 0 ] ) ; } else { smartContracts = await lifecycleChannel . getAllCommittedSmartContracts ( peerNames [ 0 ] ) ; } return smartContracts . map ( ( smartContract ) => { return { name : smartContract . smartContractName , version : smartContract . smartContractVersion , sequence : smartContract . sequence } ; } ) ; } public disconnect ( ) { EvaluateQueryHandler . setPeers ( undefined ) ; this . gateway . disconnect ( ) ; } public async createChannelMap ( ) { try { const allPeerNames = this . getAllPeerNames ( ) ; if ( allPeerNames . length === 0 ) { throw new Error ( 'Could not find any peers to query the list of channels from' ) ; } const channelMap = new Map < string , Array < string > > ( ) ; for ( const peerName of allPeerNames ) { const channels = await this . getAllChannelsForPeer ( peerName ) ; for ( const channelName of channels ) { let peers = channelMap . get ( channelName ) ; if ( peers ) { peers . push ( peerName ) ; channelMap . set ( channelName , peers ) ; } else { peers = [ peerName ] ; channelMap . set ( channelName , peers ) ; } } } return channelMap ; } catch ( error ) { if ( error . message && error . message . includes ( 'Received http2 header with status: 503' ) ) { throw new Error ( `Cannot connect to Fabric: ${ error . message }` ) ; } else { throw new Error ( `Error querying channel list: ${ error . message }` ) ; } } } public async getChannelPeersInfo ( channelName ) { try { const network = await this . gateway . getNetwork ( channelName ) ; const channel = network . getChannel ( ) ; const channelPeers = channel . getEndorsers ( ) ; const peerInfo = [ ] ; for ( const peer of channelPeers ) { const name = peer . name ; const mspID = peer . mspid ; peerInfo . push ( { name , mspID } ) ; } return peerInfo ; } catch ( error ) { throw new Error ( `Unable to get channel peers info: ${ error . message }` ) ; } } public async getChannelCapabilityFromPeer ( channelName , peerName ) { try { const peer = this . lifecycle . getPeer ( peerName , this . gateway . getOptions ( ) . wallet , this . gateway . getOptions ( ) . identity as string ) ; const capabilities = await peer . getChannelCapabilities ( channelName ) ; return capabilities ; } catch ( error ) { throw new Error ( `Unable to determine channel capabilities of channel ${ channelName }: ${ error . message }` ) ; } } protected async connectInner ( connectionProfile , wallet , identityName , timeout ) { this . discoveryAsLocalhost = this . hasLocalhostURLs ( connectionProfile ) ; this . discoveryEnabled = true ; this . knownChannels = connectionProfile [ 'channels' ] || { } ; const options = { wallet : wallet , identity : identityName , discovery : { asLocalhost : this . discoveryAsLocalhost , enabled : this . discoveryEnabled } , eventHandlerOptions : { commitTimeout : timeout } , queryHandlerOptions : { strategy : EvaluateQueryHandler . createQueryHandler } } ; await this . gateway . connect ( connectionProfile , options ) ; const endorsers = this . gateway [ 'client' ] . getEndorsers ( ) ; for ( const endorser of endorsers ) { const name = endorser . name ; const mspid = endorser . mspid ; const url = endorser . endpoint [ 'url' ] ; const pem = endorser . endpoint [ 'options' ] . pem ; const apiOptions = { } ; for ( const [ key , value ] of Object . entries ( endorser . endpoint [ 'options' ] ) ) { if ( key . startsWith ( 'grpc.' ) ) { apiOptions [ key ] = value ; } } this . lifecycle . addPeer ( { name , mspid , url , pem , apiOptions } ) ; } } protected async getChannelPeers ( channelName , peerNames ) { try { const network = await this . gateway . getNetwork ( channelName ) ; const channel = network . getChannel ( ) ; const channelPeers = [ ] ; for ( const name of peerNames ) { const peer = channel . getEndorser ( name ) ; channelPeers . push ( peer ) ; } return channelPeers ; } catch ( error ) { throw new Error ( `Unable to get channel peers: ${ error . message }` ) ; } } private isLocalhostURL ( url ) { const parsedURL = new URL ( url ) ; const localhosts = [ 'localhost' , '127.0.0.1' ] ; return localhosts . indexOf ( parsedURL . hostname ) !== - 1 ; } private hasLocalhostURLs ( connectionProfile ) { const urls = [ ] ; for ( const nodeType of [ 'orderers' , 'peers' , 'certificateAuthorities' ] ) { if ( ! connectionProfile [ nodeType ] ) { continue ; } const nodes = connectionProfile [ nodeType ] ; for ( const nodeName in nodes ) { if ( ! nodes [ nodeName ] . url ) { continue ; } urls . push ( nodes [ nodeName ] . url ) ; } } return urls . some ( ( url ) => this . isLocalhostURL ( url ) ) ; } private getAllPeers ( ) { return this . gateway [ 'client' ] . getEndorsers ( ) ; } }