Skip to content

Commit

Permalink
feat: credo demo integration
Browse files Browse the repository at this point in the history
Signed-off-by: Berend Sliedrecht <[email protected]>
  • Loading branch information
Berend Sliedrecht committed Sep 27, 2024
1 parent 39b0f85 commit 4d3901f
Show file tree
Hide file tree
Showing 13 changed files with 1,352 additions and 47 deletions.
10 changes: 9 additions & 1 deletion example/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,15 @@
"resizeMode": "contain",
"backgroundColor": "#ffffff"
},
"plugins": ["@animo-id/react-native-ble-didcomm"],
"plugins": [
"@animo-id/react-native-ble-didcomm",
[
"expo-camera",
{
"cameraPermission": "Allow $(PRODUCT_NAME) to access your camera"
}
]
],
"ios": {
"supportsTablet": true,
"bundleIdentifier": "id.animo.BluetoothDidcommExample"
Expand Down
15 changes: 15 additions & 0 deletions example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,32 @@
"version": "1.0.0",
"main": "index.js",
"scripts": {
"issue": "ts-node scripts/issueCredential.ts",
"start": "expo start",
"android": "expo run:android",
"prebuild": "expo prebuild",
"ios": "expo run:ios"
},
"dependencies": {
"@animo-id/react-native-ble-didcomm": "workspace:*",
"@credo-ts/anoncreds": "^0.5.11",
"@credo-ts/askar": "^0.5.11",
"@credo-ts/core": "^0.5.11",
"@credo-ts/indy-vdr": "^0.5.11",
"@credo-ts/node": "^0.5.11",
"@credo-ts/react-native": "^0.5.11",
"@hyperledger/anoncreds-nodejs": "^0.2.4",
"@hyperledger/anoncreds-react-native": "^0.2.4",
"@hyperledger/aries-askar-nodejs": "^0.2.3",
"@hyperledger/aries-askar-react-native": "^0.2.3",
"@hyperledger/indy-vdr-nodejs": "^0.2.2",
"@hyperledger/indy-vdr-react-native": "^0.2.2",
"expo": "*",
"expo-camera": "~15.0.16",
"expo-status-bar": "~1.12.1",
"fast-text-encoding": "^1.0.6",
"qrcode": "^1.5.4",
"qrcode-terminal": "^0.12.0",
"react": "*",
"react-native": "*",
"react-native-fs": "^2.20.0",
Expand All @@ -25,7 +37,10 @@
"devDependencies": {
"@babel/core": "^7.20.0",
"@types/fast-text-encoding": "^1.0.3",
"@types/qrcode": "^1.5.5",
"@types/qrcode-terminal": "^0.12.2",
"babel-plugin-module-resolver": "^5.0.2",
"ts-node": "^10.9.2",
"typescript": "*"
},
"private": true
Expand Down
174 changes: 174 additions & 0 deletions example/scripts/issueCredential.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
import { AnonCredsCredentialFormatService, AnonCredsModule } from '@credo-ts/anoncreds'
import { AskarModule } from '@credo-ts/askar'
import {
Agent,
AutoAcceptCredential,
ConnectionEventTypes,
type ConnectionStateChangedEvent,
ConnectionsModule,
ConsoleLogger,
CredentialEventTypes,
CredentialState,
type CredentialStateChangedEvent,
CredentialsModule,
DidExchangeState,
DidsModule,
HttpOutboundTransport,
KeyType,
LogLevel,
TypedArrayEncoder,
V2CredentialProtocol,
WsOutboundTransport,
} from '@credo-ts/core'
import {
IndyVdrAnonCredsRegistry,
IndyVdrIndyDidRegistrar,
IndyVdrIndyDidResolver,
IndyVdrModule,
} from '@credo-ts/indy-vdr'
import { HttpInboundTransport, agentDependencies } from '@credo-ts/node'
import { anoncreds } from '@hyperledger/anoncreds-nodejs'
import { ariesAskar } from '@hyperledger/aries-askar-nodejs'
import { indyVdr } from '@hyperledger/indy-vdr-nodejs'
import QRCode from 'qrcode'
import { BC_GOV_TEST_NET } from '../src/credo/utils/genesis'

const modules = {
askar: new AskarModule({ ariesAskar }),
indyVdr: new IndyVdrModule({
indyVdr,
networks: [
{
isProduction: false,
indyNamespace: 'bcovrin:test',
genesisTransactions: BC_GOV_TEST_NET,
},
],
}),
anoncreds: new AnonCredsModule({
anoncreds,
registries: [new IndyVdrAnonCredsRegistry()],
}),
dids: new DidsModule({
resolvers: [new IndyVdrIndyDidResolver()],
registrars: [new IndyVdrIndyDidRegistrar()],
}),
connections: new ConnectionsModule({ autoAcceptConnections: true }),
credentials: new CredentialsModule({
autoAcceptCredentials: AutoAcceptCredential.Always,
credentialProtocols: [
new V2CredentialProtocol({
credentialFormats: [new AnonCredsCredentialFormatService()],
}),
],
}),
}

void (async () => {
const agent = new Agent({
config: {
label: 'nodejs-register-agent',
walletConfig: { id: 'nodejs-register-agent', key: 'nodejs-register-key' },
logger: new ConsoleLogger(LogLevel.off),
endpoints: ['https://36ba-161-51-75-238.ngrok-free.app'],
},
modules,
dependencies: agentDependencies,
})

agent.registerOutboundTransport(new WsOutboundTransport())
agent.registerOutboundTransport(new HttpOutboundTransport())
agent.registerInboundTransport(new HttpInboundTransport({ port: 3001 }))

await agent.initialize()

const { outOfBandInvitation } = await agent.oob.createInvitation()
const url = outOfBandInvitation.toUrl({ domain: 'https://example.org' })
const qrcode = await QRCode.toString(url, { type: 'terminal', small: true })
console.log(qrcode)

agent.events.on<ConnectionStateChangedEvent>(
ConnectionEventTypes.ConnectionStateChanged,
async ({ payload: { connectionRecord } }) => {
console.log(`NEW CONNECTION STATE: ${connectionRecord.state}`)
if (connectionRecord.state === DidExchangeState.Completed) {
await issue(agent, connectionRecord.id)
}
}
)

agent.events.on<CredentialStateChangedEvent>(
CredentialEventTypes.CredentialStateChanged,
async ({ payload: { credentialRecord } }) => {
console.log(`NEW CREDENTIAL STATE: ${credentialRecord.state}`)
if (credentialRecord.state === CredentialState.Done) {
console.log('Issued credential!')
process.exit(0)
}
}
)
})()

const issue = async (agent: Agent<typeof modules>, connectionId: string) => {
const seed = TypedArrayEncoder.fromString('abbakabba00000000000000000000000')
const unqualifiedIndyDid = 'NyKUCBjGVmmaUopiXAnLpj'
const indyDid = `did:indy:bcovrin:test:${unqualifiedIndyDid}`
await agent.dids.import({
did: indyDid,
overwrite: true,
privateKeys: [{ keyType: KeyType.Ed25519, privateKey: seed }],
})

const schemaResult = await agent.modules.anoncreds.registerSchema({
schema: {
name: 'react-native-ble-didcomm-schema',
version: `1.0.${Date.now()}`,
attrNames: ['name', 'age'],
issuerId: indyDid,
},
options: {},
})

if (schemaResult.schemaState.state === 'failed') {
throw new Error(`Error creating schema: ${schemaResult.schemaState.reason}`)
}

console.log('registered schema')

const credentialDefinitionResult = await agent.modules.anoncreds.registerCredentialDefinition({
credentialDefinition: {
tag: 'default',
issuerId: indyDid,
schemaId: schemaResult.schemaState.schemaId,
},
options: {
supportRevocation: false,
},
})

if (credentialDefinitionResult.credentialDefinitionState.state === 'failed') {
throw new Error(
`Error creating credential definition: ${credentialDefinitionResult.credentialDefinitionState.reason}`
)
}

if (!credentialDefinitionResult.credentialDefinitionState.credentialDefinitionId) {
throw new Error('Could not find credential definition id on state')
}

console.log('registered credential definition!')

await agent.credentials.offerCredential({
protocolVersion: 'v2',
connectionId: connectionId,
credentialFormats: {
anoncreds: {
credentialDefinitionId: credentialDefinitionResult.credentialDefinitionState.credentialDefinitionId,
attributes: [
{ name: 'name', value: 'Jane Doe' },
{ name: 'age', value: '23' },
],
},
},
})
}
79 changes: 79 additions & 0 deletions example/src/credo/Camera.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { type CameraType, CameraView, useCameraPermissions } from 'expo-camera'
import { useState } from 'react'
import { Button, StyleSheet, Text, TouchableOpacity, View } from 'react-native'

type CameraProps = {
onQrScanned: (data: string) => Promise<void> | void
}

export const Camera: React.FC<CameraProps> = ({ onQrScanned }) => {
const [facing, setFacing] = useState<CameraType>('back')
const [permission, requestPermission] = useCameraPermissions()

if (!permission) {
return (
<View>
<Text>Loading...</Text>
</View>
)
}

if (!permission.granted) {
return (
<View style={styles.container}>
<Text style={styles.message}>We need your permission to show the camera</Text>
<Button onPress={requestPermission} title="grant permission" />
</View>
)
}
function toggleCameraFacing() {
setFacing((current) => (current === 'back' ? 'front' : 'back'))
}

return (
<View style={styles.container}>
<CameraView
style={styles.camera}
barcodeScannerSettings={{ barcodeTypes: ['qr'] }}
onBarcodeScanned={({ data }) => onQrScanned(data)}
facing={facing}
>
<View style={styles.buttonContainer}>
<TouchableOpacity style={styles.button} onPress={toggleCameraFacing}>
<Text style={styles.text}>Flip Camera</Text>
</TouchableOpacity>
</View>
</CameraView>
</View>
)
}

const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
},
message: {
textAlign: 'center',
paddingBottom: 10,
},
camera: {
flex: 1,
},
buttonContainer: {
flex: 1,
flexDirection: 'row',
backgroundColor: 'transparent',
margin: 64,
},
button: {
flex: 1,
alignSelf: 'flex-end',
alignItems: 'center',
},
text: {
fontSize: 24,
fontWeight: 'bold',
color: 'white',
},
})
10 changes: 5 additions & 5 deletions example/src/credo/CredoScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import * as React from 'react'
import { type ReactElement, useEffect, useState } from 'react'
import { Button, Text } from 'react-native'
import { Spacer } from '../Spacer'
import { Issuer } from './Issuer'
import { Prover } from './Prover'
import { Verifier } from './Verifier'
import { type AppAgent, setupAgent } from './agent'

export const CredoScreen = () => {
const [role, setRole] = useState<'issuer' | 'verifier'>()
const [role, setRole] = useState<'prover' | 'verifier'>()
const [agent, setAgent] = useState<AppAgent>()

useEffect(() => {
Expand All @@ -19,7 +19,7 @@ export const CredoScreen = () => {
if (!role) {
component = (
<>
<Button title="issuer" onPress={() => setRole('issuer')} />
<Button title="prover" onPress={() => setRole('prover')} />
<Spacer />
<Button title="verifier" onPress={() => setRole('verifier')} />
</>
Expand All @@ -30,8 +30,8 @@ export const CredoScreen = () => {
component = <Text>Setting up agent...</Text>
}

if (role === 'issuer' && agent) {
component = <Issuer agent={agent} />
if (role === 'prover' && agent) {
component = <Prover agent={agent} />
}

if (role === 'verifier' && agent) {
Expand Down
15 changes: 0 additions & 15 deletions example/src/credo/Issuer.tsx

This file was deleted.

Loading

0 comments on commit 4d3901f

Please sign in to comment.