Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): connection-less issuance and verification #359

Merged
merged 22 commits into from
Jul 22, 2021
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
dd0adb4
feat: add ~service decorator
TimoGlastra Jun 25, 2021
1ecaa73
feat: support connection-less issuance
TimoGlastra Jun 26, 2021
6a860d1
test: add connection-less credential exchange test
TimoGlastra Jun 26, 2021
17a46d1
feat: support connection-less verification
TimoGlastra Jul 3, 2021
0e8fc5a
style: eslint warnings
TimoGlastra Jul 3, 2021
ae00e7a
test: add tests for assertConnectionOrServiceDecorator
TimoGlastra Jul 3, 2021
858ed99
docs: update features in readme
TimoGlastra Jul 3, 2021
17031e5
Merge remote-tracking branch 'upstream/main' into feat/conectionless
TimoGlastra Jul 17, 2021
e6820a3
style: eslint warnings
TimoGlastra Jul 17, 2021
1730586
fix: updates after merge
TimoGlastra Jul 17, 2021
38244db
test: only log on error
TimoGlastra Jul 17, 2021
7ff329a
test: add timeout before shutdown of mediator
TimoGlastra Jul 17, 2021
869d4d1
Merge remote-tracking branch 'upstream/main' into feat/conectionless
TimoGlastra Jul 20, 2021
6fc2589
chore(core): update to work with auto-accept proofs
TimoGlastra Jul 20, 2021
b91dcd4
Merge remote-tracking branch 'upstream/main' into feat/conectionless
TimoGlastra Jul 20, 2021
75d9bb9
test(core): add connectionles auto accept proof case
TimoGlastra Jul 20, 2021
a6206b8
test(core): fix tests
TimoGlastra Jul 21, 2021
08c6d69
test(core): longer wait for shutdown
TimoGlastra Jul 21, 2021
330b4f5
fix(core): start with $stop event
TimoGlastra Jul 21, 2021
8d2f1cc
Merge remote-tracking branch 'upstream/main' into feat/conectionless
TimoGlastra Jul 22, 2021
a504079
test: break up e2e test to prevent flakyness
TimoGlastra Jul 22, 2021
707034e
build: tsconfig updates
TimoGlastra Jul 22, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,12 @@ Some features are not yet supported, but are on our roadmap. Check [the roadmap]
- ✅ Basic Message Protocol ([RFC 0095](https://github.com/hyperledger/aries-rfcs/blob/master/features/0095-basic-message/README.md))
- ✅ Indy Credentials (with `did:sov` support)
- ✅ HTTP Transport
- ✅ Connection-less Issuance and Verification
- 🚧 Revocation of Indy Credentials
- 🚧 Electron
- 🚧 Mediator Coordination Protocol ([RFC 0211](https://github.com/hyperledger/aries-rfcs/blob/master/features/0211-route-coordination/README.md))
- 🚧 WebSocket Transport
- ❌ Browser
- ❌ Connection-less Issuance and Verification
- ❌ Issue Credential V2, Present Proof V2, DID Exchange Protocol, Out-Of-Band
- ❌ W3C Linked Data VCs, BBS+ Signatures

Expand Down
158 changes: 158 additions & 0 deletions src/__tests__/connectionless-credentials.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import { Subject } from 'rxjs'

import { Agent } from '../agent/Agent'
import {
CredentialRecord,
CredentialState,
CredentialPreview,
CredentialPreviewAttribute,
} from '../modules/credentials'

import {
ensurePublicDidIsOnLedger,
registerDefinition,
registerSchema,
SubjectInboundTransporter,
SubjectOutboundTransporter,
waitForCredentialRecord,
genesisPath,
getBaseConfig,
} from './helpers'
import testLogger from './logger'

const faberConfig = getBaseConfig('Faber connection-less Credentials', {
genesisPath,
})

const aliceConfig = getBaseConfig('Alice connection-less Credentials', {
genesisPath,
})

const credentialPreview = new CredentialPreview({
attributes: [
new CredentialPreviewAttribute({
name: 'name',
mimeType: 'text/plain',
value: 'John',
}),
new CredentialPreviewAttribute({
name: 'age',
mimeType: 'text/plain',
value: '99',
}),
],
})

describe('credentials', () => {
let faberAgent: Agent
let aliceAgent: Agent
let credDefId: string

beforeAll(async () => {
const faberMessages = new Subject()
const aliceMessages = new Subject()

faberAgent = new Agent(faberConfig)
faberAgent.setInboundTransporter(new SubjectInboundTransporter(faberMessages, aliceMessages))
faberAgent.setOutboundTransporter(new SubjectOutboundTransporter(aliceMessages))
await faberAgent.init()

aliceAgent = new Agent(aliceConfig)
aliceAgent.setInboundTransporter(new SubjectInboundTransporter(aliceMessages, faberMessages))
aliceAgent.setOutboundTransporter(new SubjectOutboundTransporter(faberMessages))
await aliceAgent.init()

const schemaTemplate = {
name: `test-schema-${Date.now()}`,
attributes: ['name', 'age'],
version: '1.0',
}
const schema = await registerSchema(faberAgent, schemaTemplate)

const definitionTemplate = {
schema,
tag: 'TAG',
signatureType: 'CL' as const,
supportRevocation: false,
}
const credentialDefinition = await registerDefinition(faberAgent, definitionTemplate)
credDefId = credentialDefinition.id

const publicDid = faberAgent.publicDid?.did
await ensurePublicDidIsOnLedger(faberAgent, publicDid!)
})

afterAll(async () => {
await faberAgent.closeAndDeleteWallet()
await aliceAgent.closeAndDeleteWallet()
})

test('Faber starts with connection-less credential offer to Alice', async () => {
testLogger.test('Faber sends credential offer to Alice')
// eslint-disable-next-line prefer-const
let { offerMessage, credentialRecord: faberCredentialRecord } = await faberAgent.credentials.createOutOfBandOffer({
preview: credentialPreview,
credentialDefinitionId: credDefId,
comment: 'some comment about credential',
})

const credentialRecordPromise = waitForCredentialRecord(aliceAgent, {
threadId: faberCredentialRecord.threadId,
state: CredentialState.OfferReceived,
})

await aliceAgent.receiveMessage(offerMessage.toJSON())

testLogger.test('Alice waits for credential offer from Faber')
let aliceCredentialRecord = await credentialRecordPromise

testLogger.test('Alice sends credential request to Faber')
aliceCredentialRecord = await aliceAgent.credentials.acceptOffer(aliceCredentialRecord.id)

testLogger.test('Faber waits for credential request from Alice')
faberCredentialRecord = await waitForCredentialRecord(faberAgent, {
threadId: aliceCredentialRecord.threadId,
state: CredentialState.RequestReceived,
})

testLogger.test('Faber sends credential to Alice')
faberCredentialRecord = await faberAgent.credentials.acceptRequest(faberCredentialRecord.id)

testLogger.test('Alice waits for credential from Faber')
aliceCredentialRecord = await waitForCredentialRecord(aliceAgent, {
threadId: faberCredentialRecord.threadId,
state: CredentialState.CredentialReceived,
})

testLogger.test('Alice sends credential ack to Faber')
aliceCredentialRecord = await aliceAgent.credentials.acceptCredential(aliceCredentialRecord.id)

testLogger.test('Faber waits for credential ack from Alice')
faberCredentialRecord = await waitForCredentialRecord(faberAgent, {
threadId: faberCredentialRecord.threadId,
state: CredentialState.Done,
})

expect(aliceCredentialRecord).toMatchObject({
type: CredentialRecord.name,
id: expect.any(String),
createdAt: expect.any(Date),
offerMessage: expect.any(Object),
requestMessage: expect.any(Object),
metadata: { requestMetadata: expect.any(Object) },
credentialId: expect.any(String),
state: CredentialState.Done,
threadId: expect.any(String),
})

expect(faberCredentialRecord).toMatchObject({
type: CredentialRecord.name,
id: expect.any(String),
createdAt: expect.any(Date),
offerMessage: expect.any(Object),
requestMessage: expect.any(Object),
state: CredentialState.Done,
threadId: expect.any(String),
})
})
})
194 changes: 194 additions & 0 deletions src/__tests__/connectionless-proofs.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
import type { CredDefId } from 'indy-sdk'

import { Subject } from 'rxjs'

import { Agent } from '../agent/Agent'
import { CredentialPreview, CredentialPreviewAttribute } from '../modules/credentials'
import {
PredicateType,
PresentationPreview,
PresentationPreviewAttribute,
PresentationPreviewPredicate,
ProofState,
ProofAttributeInfo,
AttributeFilter,
ProofPredicateInfo,
} from '../modules/proofs'

import {
ensurePublicDidIsOnLedger,
registerDefinition,
registerSchema,
SubjectInboundTransporter,
SubjectOutboundTransporter,
genesisPath,
waitForProofRecord,
getBaseConfig,
issueConnectionLessCredential,
} from './helpers'
import testLogger from './logger'

const faberConfig = getBaseConfig('Faber connection-less Proofs', { genesisPath })
const aliceConfig = getBaseConfig('Alice connection-less Proofs', { genesisPath })

const credentialPreview = new CredentialPreview({
attributes: [
new CredentialPreviewAttribute({
name: 'name',
mimeType: 'text/plain',
value: 'John',
}),
new CredentialPreviewAttribute({
name: 'age',
mimeType: 'text/plain',
value: '99',
}),
],
})

describe('Present Proof', () => {
let faberAgent: Agent
let aliceAgent: Agent
let credDefId: CredDefId
let presentationPreview: PresentationPreview

beforeAll(async () => {
const faberMessages = new Subject()
const aliceMessages = new Subject()

faberAgent = new Agent(faberConfig)
faberAgent.setInboundTransporter(new SubjectInboundTransporter(faberMessages, aliceMessages))
faberAgent.setOutboundTransporter(new SubjectOutboundTransporter(aliceMessages))
await faberAgent.init()

aliceAgent = new Agent(aliceConfig)
aliceAgent.setInboundTransporter(new SubjectInboundTransporter(aliceMessages, faberMessages))
aliceAgent.setOutboundTransporter(new SubjectOutboundTransporter(faberMessages))
await aliceAgent.init()

const schemaTemplate = {
name: `test-schema-${Date.now()}`,
attributes: ['name', 'age'],
version: '1.0',
}
const schema = await registerSchema(faberAgent, schemaTemplate)

const definitionTemplate = {
schema,
tag: 'TAG',
signatureType: 'CL' as const,
supportRevocation: false,
}
const credentialDefinition = await registerDefinition(faberAgent, definitionTemplate)
credDefId = credentialDefinition.id

const publicDid = faberAgent.publicDid?.did
await ensurePublicDidIsOnLedger(faberAgent, publicDid!)

presentationPreview = new PresentationPreview({
attributes: [
new PresentationPreviewAttribute({
name: 'name',
credentialDefinitionId: credDefId,
referent: '0',
value: 'John',
}),
],
predicates: [
new PresentationPreviewPredicate({
name: 'age',
credentialDefinitionId: credDefId,
predicate: PredicateType.GreaterThanOrEqualTo,
threshold: 50,
}),
],
})

await issueConnectionLessCredential({
issuerAgent: faberAgent,
holderAgent: aliceAgent,
credentialTemplate: {
credentialDefinitionId: credDefId,
comment: 'some comment about credential',
preview: credentialPreview,
},
})
})

afterAll(async () => {
await faberAgent.closeAndDeleteWallet()
await aliceAgent.closeAndDeleteWallet()
})

test('Faber starts with proof requests to Alice', async () => {
testLogger.test('Faber sends presentation request to Alice')

const attributes = {
name: new ProofAttributeInfo({
name: 'name',
restrictions: [
new AttributeFilter({
credentialDefinitionId: credDefId,
}),
],
}),
}

const predicates = {
age: new ProofPredicateInfo({
name: 'age',
predicateType: PredicateType.GreaterThanOrEqualTo,
predicateValue: 50,
restrictions: [
new AttributeFilter({
credentialDefinitionId: credDefId,
}),
],
}),
}

// eslint-disable-next-line prefer-const
let { proofRecord: faberProofRecord, requestMessage } = await faberAgent.proofs.createOutOfBandRequest({
name: 'test-proof-request',
requestedAttributes: attributes,
requestedPredicates: predicates,
})

const aliceProofRecordPromise = waitForProofRecord(aliceAgent, {
threadId: faberProofRecord.threadId,
state: ProofState.RequestReceived,
})

await aliceAgent.receiveMessage(requestMessage.toJSON())

testLogger.test('Alice waits for presentation request from Faber')
let aliceProofRecord = await aliceProofRecordPromise

testLogger.test('Alice accepts presentation request from Faber')
const indyProofRequest = aliceProofRecord.requestMessage?.indyProofRequest
const retrievedCredentials = await aliceAgent.proofs.getRequestedCredentialsForProofRequest(
indyProofRequest!,
presentationPreview
)
const requestedCredentials = aliceAgent.proofs.autoSelectCredentialsForProofRequest(retrievedCredentials)
await aliceAgent.proofs.acceptRequest(aliceProofRecord.id, requestedCredentials)

testLogger.test('Faber waits for presentation from Alice')
faberProofRecord = await waitForProofRecord(faberAgent, {
threadId: aliceProofRecord.threadId,
state: ProofState.PresentationReceived,
})

// assert presentation is valid
expect(faberProofRecord.isVerified).toBe(true)

// Faber accepts presentation
await faberAgent.proofs.acceptPresentation(faberProofRecord.id)

// Alice waits till it receives presentation ack
aliceProofRecord = await waitForProofRecord(aliceAgent, {
threadId: aliceProofRecord.threadId,
state: ProofState.Done,
})
})
})
Loading