Skip to content

Commit

Permalink
Integrating PLC lib (bluesky-social#607)
Browse files Browse the repository at this point in the history
* integrating new plc lib

* patching up did-resolver

* buffing up pds tests

* didResolver on ctx & plc in postgres

* bring dev env up to date

* re-add extension for linting

* tidy

* use current env vars

* pr feedback
  • Loading branch information
dholms authored Mar 5, 2023
1 parent 3bd50b0 commit 8dfcb4f
Show file tree
Hide file tree
Showing 66 changed files with 298 additions and 2,590 deletions.
52 changes: 0 additions & 52 deletions .github/workflows/build-and-push-plc.yaml

This file was deleted.

5 changes: 0 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,6 @@ run-pds: ## Run PDS locally
if [ ! -f "packages/pds/.dev.env" ]; then cp packages/pds/example.dev.env packages/pds/.dev.env; fi
cd packages/pds; ENV=dev yarn run start | yarn exec pino-pretty

.PHONY: run-plc
run-plc: ## Run DID:PLC server locally
if [ ! -f "packages/plc/.dev.env" ]; then cp packages/plc/example.dev.env packages/plc/.dev.env; fi
cd packages/plc; ENV=dev yarn run start | yarn exec pino-pretty

.PHONY: lint
lint: ## Run style checks and verify syntax
yarn verify
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"esbuild-plugin-copy": "^1.6.0",
"eslint": "^8.24.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.2.1",
"jest": "^28.1.2",
"lerna": "^4.0.0",
"npm-run-all": "^4.1.5",
Expand Down
1 change: 0 additions & 1 deletion packages/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
- [DID Resolver](./did-resolver): A library for resolving ATP's Decentralized ID methods.
- [Lexicon](./lexicon): A library for validating data using ATP's schema system.
- [NSID](./nsid): A parser and generator of NSIDs.
- [PLC](./plc): The did:placeholder implementation.
- [Repo](./repo): The "ATP repository" core implementation (a Merkle Search Tree).
- [URI](./uri): A parser and generator of `at://` uris.
- [XRPC](./xrpc): An XRPC client implementation.
Expand Down
2 changes: 2 additions & 0 deletions packages/dev-env/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
"@atproto/did-resolver": "*",
"@atproto/pds": "*",
"@atproto/uri": "*",
"@did-plc/lib": "^0.0.1",
"@did-plc/server": "^0.0.1",
"chalk": "^5.0.1",
"dotenv": "^16.0.1",
"get-port": "^6.1.2",
Expand Down
26 changes: 14 additions & 12 deletions packages/dev-env/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import PDSServer, {
MemoryBlobStore,
ServerConfig as PDSServerConfig,
} from '@atproto/pds'
import * as plc from '@atproto/plc'
import * as plc from '@did-plc/lib'
import { PlcServer, Database as PlcDatabase } from '@did-plc/server'
import * as crypto from '@atproto/crypto'
import AtpAgent from '@atproto/api'
import { ServerType, ServerConfig, StartParams } from './types.js'
Expand Down Expand Up @@ -70,18 +71,20 @@ export class DevEnvServer {

const blobstore = new MemoryBlobStore()

const plcClient = new plc.PlcClient(this.env.plcUrl)
const serverDid = await plcClient.createDid(
keypair,
keypair.did(),
'localhost',
`http://localhost:${this.port}`,
)
const plcClient = new plc.Client(this.env.plcUrl)
const serverDid = await plcClient.createDid({
signingKey: keypair.did(),
rotationKeys: [keypair.did()],
handle: 'localhost',
pds: `http://localhost:${this.port}`,
signer: keypair,
})

const pds = PDSServer.create({
db,
blobstore,
keypair,
repoSigningKey: keypair,
plcRotationKey: keypair,
config: new PDSServerConfig({
debugMode: true,
version: '0.0.0',
Expand Down Expand Up @@ -112,9 +115,8 @@ export class DevEnvServer {
break
}
case ServerType.DidPlaceholder: {
const db = plc.Database.memory()
await db.migrateToLatestOrThrow()
const plcServer = plc.PlcServer.create({ db, port: this.port })
const db = PlcDatabase.mock()
const plcServer = PlcServer.create({ db, port: this.port })
await startServer(plcServer)
this.inst = plcServer
break
Expand Down
3 changes: 2 additions & 1 deletion packages/did-resolver/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
"did-resolver": "^4.0.0"
},
"devDependencies": {
"@atproto/plc": "*",
"@did-plc/lib": "^0.0.1",
"@did-plc/server": "^0.0.1",
"cors": "^2.8.5",
"express": "^4.18.2",
"get-port": "^6.1.2"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { DIDDocument } from 'did-resolver'
import * as crypto from '@atproto/crypto'

export type AtpData = {
export type AtprotoData = {
did: string
signingKey: string
recoveryKey: string
handle: string
atpPds: string
pds: string
}

export const getDid = (doc: DIDDocument): string => {
Expand All @@ -17,14 +16,14 @@ export const getDid = (doc: DIDDocument): string => {
return id
}

export const getKey = (doc: DIDDocument, id: string): string | undefined => {
export const getKey = (doc: DIDDocument): string | undefined => {
let keys = doc.verificationMethod
if (!keys) return undefined
if (typeof keys !== 'object') return undefined
if (!Array.isArray(keys)) {
keys = [keys]
}
const found = keys.find((key) => key.id === id)
const found = keys.find((key) => key.id === '#atproto')
if (!found) return undefined

// @TODO support jwk
Expand All @@ -43,66 +42,55 @@ export const getKey = (doc: DIDDocument, id: string): string | undefined => {
export const getHandle = (doc: DIDDocument): string | undefined => {
const aka = doc.alsoKnownAs
if (!aka) return undefined
let found: string | undefined
if (typeof aka === 'string') found = aka
if (Array.isArray(aka) && typeof aka[0] === 'string') {
found = aka[0]
}
const found = aka.find((name) => name.startsWith('at://'))
if (!found) return undefined
return new URL(found).host
// strip off at:// prefix
return found.slice(5)
}

export const getAtpPds = (doc: DIDDocument): string | undefined => {
export const getPds = (doc: DIDDocument): string | undefined => {
let services = doc.service
if (!services) return undefined
if (typeof services !== 'object') return undefined
if (!Array.isArray(services)) {
services = [services]
}
const found = services.find(
(service) => service.type === 'AtpPersonalDataServer',
)
const found = services.find((service) => service.id === '#atproto_pds')
if (!found) return undefined
if (found.type !== 'AtprotoPersonalDataServer') {
return undefined
}
if (typeof found.serviceEndpoint === 'string') {
return found.serviceEndpoint
} else if (
Array.isArray(found.serviceEndpoint) &&
typeof found.serviceEndpoint[0] === 'string'
) {
return found.serviceEndpoint[0]
} else {
return undefined
}
return undefined
}

export const parseToAtpDocument = (doc: DIDDocument): Partial<AtpData> => {
export const parseToAtprotoDocument = (
doc: DIDDocument,
): Partial<AtprotoData> => {
const did = getDid(doc)
return {
did,
signingKey: getKey(doc, '#signingKey'),
recoveryKey: getKey(doc, '#recoveryKey'),
signingKey: getKey(doc),
handle: getHandle(doc),
atpPds: getAtpPds(doc),
pds: getPds(doc),
}
}

export const ensureAtpDocument = (doc: DIDDocument): AtpData => {
const { did, signingKey, recoveryKey, handle, atpPds } =
parseToAtpDocument(doc)
export const ensureAtpDocument = (doc: DIDDocument): AtprotoData => {
const { did, signingKey, handle, pds } = parseToAtprotoDocument(doc)
if (!did) {
throw new Error(`Could not parse id from doc: ${doc}`)
}
if (!signingKey) {
throw new Error(`Could not parse signingKey from doc: ${doc}`)
}
if (!recoveryKey) {
throw new Error(`Could not parse recoveryKey from doc: ${doc}`)
}
if (!handle) {
throw new Error(`Could not parse handle from doc: ${doc}`)
}
if (!atpPds) {
throw new Error(`Could not parse atpPds from doc: ${doc}`)
if (!pds) {
throw new Error(`Could not parse pds from doc: ${doc}`)
}
return { did, signingKey, recoveryKey, handle, atpPds }
return { did, signingKey, handle, pds }
}
2 changes: 1 addition & 1 deletion packages/did-resolver/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export * as web from './web-resolver'
export * as plc from './plc-resolver'
export * from './resolver'
export * from './atp-did'
export * from './atproto-data'
4 changes: 2 additions & 2 deletions packages/did-resolver/src/resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
import * as crypto from '@atproto/crypto'
import * as web from './web-resolver'
import * as plc from './plc-resolver'
import * as atpDid from './atp-did'
import * as atpDid from './atproto-data'
import log from './logger'

export type DidResolverOptions = {
Expand Down Expand Up @@ -52,7 +52,7 @@ export class DidResolver {
return result.didDocument
}

async resolveAtpData(did: string): Promise<atpDid.AtpData> {
async resolveAtpData(did: string): Promise<atpDid.AtprotoData> {
const didDocument = await this.ensureResolveDid(did)
return atpDid.ensureAtpDocument(didDocument)
}
Expand Down
50 changes: 29 additions & 21 deletions packages/did-resolver/tests/resolver.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import getPort from 'get-port'
import { AtpData, DidResolver } from '../src'
import { DidResolver } from '../src'
import { DidWebServer } from './web/server'
import DidWebDb from './web/db'
import { Database as DidPlcDb, PlcServer, PlcClient } from '@atproto/plc'
import * as plc from '@did-plc/lib'
import { Database as DidPlcDb, PlcServer } from '@did-plc/server'
import { DIDDocument } from 'did-resolver'
import { EcdsaKeypair } from '@atproto/crypto'
import { Secp256k1Keypair } from '@atproto/crypto'

describe('resolver', () => {
let close: () => Promise<void>
Expand All @@ -20,8 +21,7 @@ describe('resolver', () => {
webServer._httpServer?.on('error', reject)
})

const plcDB = DidPlcDb.memory()
await plcDB.migrateToLatestOrThrow()
const plcDB = DidPlcDb.mock()
const plcPort = await getPort()
const plcServer = PlcServer.create({ db: plcDB, port: plcPort })
await plcServer.start()
Expand All @@ -39,20 +39,26 @@ describe('resolver', () => {
await close()
})

const handle = 'alice.test'
const pds = 'https://service.test'
let signingKey: Secp256k1Keypair
let rotationKey: Secp256k1Keypair
let webDid: string
let plcDid: string
let didWebDoc: DIDDocument
let didPlcDoc: DIDDocument
let didWebData: AtpData
let didPlcData: AtpData

it('creates the did on did:web & did:plc', async () => {
const signingKey = await EcdsaKeypair.create()
const recoveryKey = await EcdsaKeypair.create()
const handle = 'alice.test'
const pds = 'https://service.test'
const client = new PlcClient(plcUrl)
plcDid = await client.createDid(signingKey, recoveryKey.did(), handle, pds)
signingKey = await Secp256k1Keypair.create()
rotationKey = await Secp256k1Keypair.create()
const client = new plc.Client(plcUrl)
plcDid = await client.createDid({
signingKey: signingKey.did(),
handle,
pds,
rotationKeys: [rotationKey.did()],
signer: rotationKey,
})
didPlcDoc = await client.getDocument(plcDid)
const domain = encodeURIComponent(`localhost:${webServer.port}`)
webDid = `did:web:${domain}`
Expand All @@ -61,12 +67,6 @@ describe('resolver', () => {
id: webDid,
}

didPlcData = await client.getDocumentData(plcDid)
didWebData = {
...didPlcData,
did: webDid,
}

await webServer.put(didWebDoc)
})

Expand All @@ -77,7 +77,11 @@ describe('resolver', () => {

it('resolve valid atpData from did:web', async () => {
const atpData = await resolver.resolveAtpData(webDid)
expect(atpData).toEqual(didWebData)
expect(atpData.did).toEqual(webDid)
expect(atpData.handle).toEqual(handle)
expect(atpData.pds).toEqual(pds)
expect(atpData.signingKey).toEqual(signingKey.did())
expect(atpData.handle).toEqual(handle)
})

it('throws on malformed did:webs', async () => {
Expand All @@ -93,7 +97,11 @@ describe('resolver', () => {

it('resolve valid atpData from did:plc', async () => {
const atpData = await resolver.resolveAtpData(plcDid)
expect(atpData).toEqual(didPlcData)
expect(atpData.did).toEqual(plcDid)
expect(atpData.handle).toEqual(handle)
expect(atpData.pds).toEqual(pds)
expect(atpData.signingKey).toEqual(signingKey.did())
expect(atpData.handle).toEqual(handle)
})

it('throws on malformed did:plc', async () => {
Expand Down
1 change: 0 additions & 1 deletion packages/did-resolver/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,5 @@
"references": [
{ "path": "../common/tsconfig.build.json" },
{ "path": "../crypto/tsconfig.build.json" },
{ "path": "../plc/tsconfig.build.json" },
]
}
Loading

0 comments on commit 8dfcb4f

Please sign in to comment.