openwallet-foundation / credo-ts

Typescript framework for building decentralized identity and verifiable credential solutions
https://credo.js.org
Apache License 2.0
260 stars 196 forks source link

Agent is not fully aware its initialized or stopped #1366

Open jleach opened 1 year ago

jleach commented 1 year ago

I don't think an AFJ 0.3.3 agent is fully aware its initialized, or that when shutdown() is called something does not realize its not initialized any longer. I also tried calling await agent?.wallet.close() before and after shutdown() but it has no effect. You can make the first warning go away by calling await agent?.mediationRecipient.stopMessagePickup() before shutdown but I think if one command initializes the wallet then one command should fully uninitialize it.

  1. Copy the TypeScript at the end of this issue to a ts file;
  2. Run with ts-node using the following command:
rm -rf ~/.indy_client/ && ./node_modules/.bin/ts-node ./myscript.ts -m $MEDIATOR_URL

Will log the following output:

LOG: will initialize agent
LOG: did initialize agent
LOG: will shutdown agent
LOG: did shutdown agent
WARN: Unable to open websocket connection to mediator {
  "error": {
    "name": "IndySdkError",
    "message": "IndyError(WalletInvalidHandle): WalletInvalidHandle",
    "stack": "IndySdkError: IndyError(WalletInvalidHandle): WalletInvalidHandle\n    at IndyStorageService.getById (/work/node_modules/@aries-framework/core/src/storage/IndyStorageService.ts:240:34)\n    at async RecipientApi.openMediationWebSocket (/work/node_modules/@aries-framework/core/src/modules/routing/RecipientApi.ts:130:24)\n    at async RecipientApi.openWebSocketAndPickUp (/work/node_modules/@aries-framework/core/src/modules/routing/RecipientApi.ts:222:9)\n    at async RecipientApi.initiateMessagePickup (/work/node_modules/@aries-framework/core/src/modules/routing/RecipientApi.ts:272:9)",
    "cause": {
      "name": "IndyError",
      "stack": "IndyError: WalletInvalidHandle\n    at Object.callback (/work/node_modules/@aries-framework/node/node_modules/indy-sdk/src/wrapIndyCallback.js:15:10)",
      "message": "WalletInvalidHandle",
      "indyCode": 200,
      "indyName": "WalletInvalidHandle",
      "indyCurrentErrorJson": null
    }
  }
}
WARN: Unable to re-open websocket connection to mediator {
  "error": {
    "name": "AriesFrameworkError",
    "message": "Wallet has not been initialized yet. Make sure to await agent.initialize() before using the agent.",
    "stack": "AriesFrameworkError: Wallet has not been initialized yet. Make sure to await agent.initialize() before using the agent.\n    at IndyWallet.get handle [as handle] (/work/node_modules/@aries-framework/core/src/wallet/IndyWallet.ts:78:13)\n    at IndyStorageService.getById (/work/node_modules/@aries-framework/core/src/storage/IndyStorageService.ts:226:29)\n    at ConnectionRepository.getById (/work/node_modules/@aries-framework/core/src/storage/Repository.ts:81:32)\n    at ConnectionService.getById (/work/node_modules/@aries-framework/core/src/modules/connections/services/ConnectionService.ts:558:38)\n    at RecipientApi.openMediationWebSocket (/work/node_modules/@aries-framework/core/src/modules/routing/RecipientApi.ts:130:53)\n    at Object.next (/work/node_modules/@aries-framework/core/src/modules/routing/RecipientApi.ts:212:26)\n    at ConsumerObserver.next (/work/node_modules/@aries-framework/core/node_modules/rxjs/src/internal/Subscriber.ts:161:25)\n    at SafeSubscriber.Subscriber._next (/work/node_modules/@aries-framework/core/node_modules/rxjs/src/internal/Subscriber.ts:119:22)\n    at SafeSubscriber.Subscriber.next (/work/node_modules/@aries-framework/core/node_modules/rxjs/src/internal/Subscriber.ts:75:12)\n    at /work/node_modules/@aries-framework/core/node_modules/rxjs/src/internal/operators/tap.ts:128:26"
  }
}

I would expect shutdown to be a clean function call and not print Make sure to await agent.initialize() before using the agent. because the code below does call this.

-- exampel script below --

import {
  Agent,
  MediatorPickupStrategy,
  AutoAcceptCredential,
  WsOutboundTransport,
  HttpOutboundTransport,
  ConsoleLogger,
  LogLevel,
} from '@aries-framework/core'
import { agentDependencies } from '@aries-framework/node'
import fs from 'fs'
import path from 'path'
import process from 'process'

const ledgersPath = path.join(__dirname, 'ledgers.json')
const ledgers = JSON.parse(fs.readFileSync(ledgersPath, 'utf8'))

enum Style {
  BrightYellow = '\x1b[33m%s\x1b[0m',
  BrightCyan = '\x1b[36m%s\x1b[0m',
  BrightRed = '\x1b[31m%s\x1b[0m'
}

const log = (message: string, style = Style.BrightYellow) => {
  console.log(style, `LOG: ${message}`)
}

const parseArgs = () => {
  let mediatorUrl: string
  const args = process.argv
  const mediatorIdx = args.indexOf('-m')

  if (isNaN(mediatorIdx) || mediatorIdx === -1) {
    log('No mediator url provided, using default.', Style.BrightCyan)
    log('usage: -m https://example.com?c_i=abc123', Style.BrightCyan)

    process.exit(-1)
  }

  mediatorUrl = args[mediatorIdx+1] 

  return { mediatorUrl }
}

const connectToMediator = async (config: any) => {
  const agent = new Agent({
    config: {
      label: "Jason Test Rig",
      logger: new ConsoleLogger(LogLevel.warn),
      mediatorConnectionsInvite: config.mediatorUrl,
      mediatorPickupStrategy: MediatorPickupStrategy.Implicit,
      autoAcceptConnections: true,
      autoAcceptCredentials: AutoAcceptCredential.ContentApproved,
      indyLedgers: ledgers,
      connectToIndyLedgersOnStartup: false,
      autoUpdateStorageOnStartup: true,
      walletConfig: {
        id: "walletId",
        key: "walletKey",
      },
    },
    dependencies: agentDependencies,
  });

  try {
    const wsTransport = new WsOutboundTransport()
    const httpTransport = new HttpOutboundTransport()

    agent.registerOutboundTransport(wsTransport)
    agent.registerOutboundTransport(httpTransport)

    return agent
  } catch (error) {
    console.log(error)
  }
}

const main = async () => {
  const config = parseArgs()
  const agent = await connectToMediator(config)

  try {
    log('will initialize agent')
    await agent?.initialize();
    log('did initialize agent')

   if (agent?.isInitialized) {
    log('will shutdown agent')
    await agent?.shutdown()
    log('did shutdown agent')
   }
  } catch (error: unknown) {
    console.log(`error = ${(error as Error).message}`);
  }
}

main()
jleach commented 1 year ago

This might not be the issue it appears to be. Looking here it seems like the warnings are being triggered by emitting an event via stop$.next(true) which throws an exception.