import * as vscode from 'vscode' ; import { VSCodeBlockchainOutputAdapter } from '../logging/VSCodeBlockchainOutputAdapter' ; import { ExtensionCommands } from '../../ExtensionCommands' ; import { FabricSmartContractDefinition , FabricEnvironmentRegistry , FabricEnvironmentRegistryEntry , IFabricEnvironmentConnection , LogType , IFabricGatewayConnection , FabricGatewayRegistry , EnvironmentType , FabricGatewayRegistryEntry , EnvironmentFlags } from 'ibm-blockchain-platform-common' ; import { URL } from 'url' ; import { FabricEnvironmentManager } from '../fabric/environments/FabricEnvironmentManager' ; import { SettingConfigurations } from '../configurations' ; import { ExtensionUtil } from '../util/ExtensionUtil' ; import { UserInputUtil , IBlockchainQuickPickItem } from '../commands/UserInputUtil' ; import { FabricGatewayConnectionManager } from '../fabric/FabricGatewayConnectionManager' ; import { LocalMicroEnvironment } from '../fabric/environments/LocalMicroEnvironment' ; import { LocalMicroEnvironmentManager } from '../fabric/environments/LocalMicroEnvironmentManager' ; export abstract class FabricDebugConfigurationProvider implements vscode . DebugConfigurationProvider { static readonly debugEvent : string = 'contractDebugging' ; static environmentName : string ; static orgName : string ; public static async getInstantiatedChaincode ( chaincodeName ) { const connection = await this . getConnection ( ) ; const allInstantiatedContracts = await connection . getAllCommittedSmartContractDefinitions ( ) ; const smartContractVersionName = allInstantiatedContracts . find ( ( contract ) => { return contract . name === chaincodeName ; } ) ; return smartContractVersionName ; } public static async connectToGateway ( ) { const gatewayConnection = FabricGatewayConnectionManager . instance ( ) . getConnection ( ) ; const fabricGatewayRegistryEntry = await FabricGatewayConnectionManager . instance ( ) . getGatewayRegistryEntry ( ) ; if ( ! gatewayConnection ) { const runtimeGateway = await FabricGatewayRegistry . instance ( ) . get ( `${ FabricDebugConfigurationProvider . environmentName } - ${ FabricDebugConfigurationProvider . orgName } Gateway` ) ; await vscode . commands . executeCommand ( ExtensionCommands . CONNECT_TO_GATEWAY , runtimeGateway ) ; if ( ! FabricGatewayConnectionManager . instance ( ) . getConnection ( ) ) { return false ; } } else if ( fabricGatewayRegistryEntry && fabricGatewayRegistryEntry . name !== `${ FabricDebugConfigurationProvider . environmentName } - ${ FabricDebugConfigurationProvider . orgName } Gateway` ) { const runtimeGateway = await FabricGatewayRegistry . instance ( ) . get ( `${ FabricDebugConfigurationProvider . environmentName } - ${ FabricDebugConfigurationProvider . orgName } Gateway` ) ; await vscode . commands . executeCommand ( ExtensionCommands . DISCONNECT_GATEWAY ) ; await vscode . commands . executeCommand ( ExtensionCommands . CONNECT_TO_GATEWAY , runtimeGateway ) ; if ( ! FabricGatewayConnectionManager . instance ( ) . getConnection ( ) ) { return false ; } } return true ; } private static async getConnection ( ) { let connection = FabricEnvironmentManager . instance ( ) . getConnection ( ) ; if ( connection ) { let environmentRegistryEntry = FabricEnvironmentManager . instance ( ) . getEnvironmentRegistryEntry ( ) ; if ( environmentRegistryEntry . name !== FabricDebugConfigurationProvider . environmentName || environmentRegistryEntry . environmentType !== EnvironmentType . LOCAL_MICROFAB_ENVIRONMENT ) { await vscode . commands . executeCommand ( ExtensionCommands . DISCONNECT_ENVIRONMENT ) ; environmentRegistryEntry = await FabricEnvironmentRegistry . instance ( ) . get ( FabricDebugConfigurationProvider . environmentName ) ; await vscode . commands . executeCommand ( ExtensionCommands . CONNECT_TO_ENVIRONMENT , environmentRegistryEntry ) ; } connection = FabricEnvironmentManager . instance ( ) . getConnection ( ) ; } else { const environmentRegistryEntry = await FabricEnvironmentRegistry . instance ( ) . get ( FabricDebugConfigurationProvider . environmentName ) ; await vscode . commands . executeCommand ( ExtensionCommands . CONNECT_TO_ENVIRONMENT , environmentRegistryEntry ) ; connection = FabricEnvironmentManager . instance ( ) . getConnection ( ) ; } if ( ! connection ) { throw new Error ( `Could not create connection to ${ FabricDebugConfigurationProvider . environmentName }` ) ; } return connection ; } private runtime : LocalMicroEnvironment ; public async resolveDebugConfiguration ( folder , config , token ? ) { const outputAdapter = VSCodeBlockchainOutputAdapter . instance ( ) ; try { if ( ExtensionUtil . isChe ( ) ) { outputAdapter . log ( LogType . ERROR , 'Debugging smart contracts is not supported in Eclipse Che or Red Hat CodeReady Workspaces.' ) ; return ; } const localFabricEnabled = ExtensionUtil . getExtensionLocalFabricSetting ( ) ; if ( ! localFabricEnabled ) { outputAdapter . log ( LogType . ERROR , `Setting '${ SettingConfigurations . EXTENSION_LOCAL_FABRIC }' must be set to 'true' to enable debugging.` ) ; return ; } const localRuntimes = [ ] ; const environmentEntries = await FabricEnvironmentRegistry . instance ( ) . getAll ( [ EnvironmentFlags . LOCAL ] ) ; for ( const entry of environmentEntries ) { const runtime = await LocalMicroEnvironmentManager . instance ( ) . ensureRuntime ( entry . name , undefined , entry . numberOfOrgs ) ; const isRunning = await runtime . isRunning ( ) ; if ( isRunning ) { localRuntimes . push ( { label : entry . name , data : runtime } ) ; } } if ( localRuntimes . length === 0 ) { outputAdapter . log ( LogType . ERROR , `No local environments found for debugging.` ) ; return ; } const chosenEnvironment = await UserInputUtil . showQuickPickItem ( 'Select a local environment to debug' , localRuntimes ) as IBlockchainQuickPickItem < LocalMicroEnvironment > ; if ( ! chosenEnvironment ) { return ; } this . runtime = chosenEnvironment . data ; FabricDebugConfigurationProvider . environmentName = this . runtime . getName ( ) ; if ( ! config . env ) { config . env = { } ; } let chaincodeVersion ; let chaincodeName ; if ( config . env . CORE_CHAINCODE_ID_NAME ) { chaincodeName = config . env . CORE_CHAINCODE_ID_NAME . split ( ':' ) [ 0 ] ; chaincodeVersion = config . env . CORE_CHAINCODE_ID_NAME . split ( ':' ) [ 1 ] ; } else { const nameAndVersion = await this . getChaincodeNameAndVersion ( folder ) ; if ( ! nameAndVersion || ! nameAndVersion . name || ! nameAndVersion . version ) { return ; } chaincodeName = nameAndVersion . name ; chaincodeVersion = nameAndVersion . version ; } const replaceRegex = /@.*?\// ; chaincodeName = chaincodeName . replace ( replaceRegex , '' ) ; const smartContract = await FabricDebugConfigurationProvider . getInstantiatedChaincode ( chaincodeName ) ; if ( smartContract ) { const isContainerRunning = await this . runtime . isRunning ( [ smartContract . name , smartContract . version ] ) ; if ( isContainerRunning ) { await vscode . window . withProgress ( { location : vscode . ProgressLocation . Notification , title : 'IBM Blockchain Platform Extension' , cancellable : false } , async ( progress ) => { progress . report ( { message : 'Removing chaincode container' } ) ; await this . runtime . killChaincode ( [ smartContract . name , smartContract . version ] ) ; } ) ; } } config . env . CORE_CHAINCODE_ID_NAME = `${ chaincodeName }:${ chaincodeVersion }` ; const resolvedConfig = await this . resolveDebugConfigurationInner ( folder , config , token ) ; if ( ! resolvedConfig ) { return ; } delete resolvedConfig . name ; resolvedConfig . debugEvent = FabricDebugConfigurationProvider . debugEvent ; const connected = await FabricDebugConfigurationProvider . connectToGateway ( ) ; if ( ! connected ) { return ; } await vscode . commands . executeCommand ( 'setContext' , 'blockchain-debug' , true ) ; vscode . debug . startDebugging ( folder , resolvedConfig ) ; return undefined ; } catch ( error ) { outputAdapter . log ( LogType . ERROR , `Failed to launch debug: ${ error . message }` ) ; return ; } } protected abstract async getChaincodeNameAndVersion ( folder ) ; protected async getChaincodeAddress ( ) { if ( this . runtime . numberOfOrgs > 1 ) { const orgNames = await this . runtime . getAllOrganizationNames ( false ) ; const mspName = await UserInputUtil . showQuickPick ( 'Select the organization to debug for' , orgNames ) as string ; if ( ! mspName ) { FabricDebugConfigurationProvider . orgName = undefined ; return ; } const _orgName = mspName . substr ( 0 , mspName . indexOf ( 'MSP' ) ) ; FabricDebugConfigurationProvider . orgName = _orgName ; } else { FabricDebugConfigurationProvider . orgName = 'Org1' ; } const url = await this . runtime . getPeerChaincodeURL ( FabricDebugConfigurationProvider . orgName ) ; const parsedURL = new URL ( url ) ; return parsedURL . host ; } protected abstract async resolveDebugConfigurationInner ( folder , config , token ? ) ; }