Skip to content

Commit

Permalink
Add protocol version (#191)
Browse files Browse the repository at this point in the history
* add required field protocol to message and resource
* add checks for protocol version to create or add to exchange
* remove jwk from generate-test-vectors
* update submodule version

Co-authored-by: Phoebe Lew <[email protected]>
  • Loading branch information
kirahsapong and phoebe-lew authored Mar 13, 2024
1 parent 1acffee commit 46481e7
Show file tree
Hide file tree
Showing 18 changed files with 172 additions and 35 deletions.
7 changes: 7 additions & 0 deletions .changeset/cyan-wolves-draw.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@tbdex/protocol": minor
"@tbdex/http-client": patch
"@tbdex/http-server": patch
---

Introduce protocol field to messages and resources
5 changes: 5 additions & 0 deletions .changeset/stale-hats-run.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@tbdex/http-client": patch
---

Update client test
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"private": true,
"type": "module",
"scripts": {
"clean": "npkill -d $(pwd) -t node_modules && npkill -d $(pwd)/packages -t dist",
"clean": "npkill -d $(pwd)/packages -t dist && npkill -d $(pwd) -t node_modules",
"build": "pnpm recursive run build",
"lint": "pnpm recursive run lint",
"lint:fix": "pnpm recursive run lint:fix",
Expand Down
20 changes: 18 additions & 2 deletions packages/protocol/src/exchange.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,18 @@ export class Exchange {
/**
* Add the next message in the exchange
* @param message - The next allowed message in the exchange
* @throws if message's protocol version does not match protocol version of other messages in the exchange
* @throws if message is not a valid next message. See {@link Exchange.isValidNext}
* @throws if message's exchangeId does not match id of the exchange
*/
addNextMessage(message: Message): void {
if (this.protocol !== undefined && message.metadata.protocol !== this.protocol) {
throw new Error(
`Could not add message (${message.metadata.id}) with protocol version ${message.metadata.protocol} to exchange because it does not have matching ` +
`protocol version ${this.protocol} as other messages in the exchange`
)
}

if (!this.isValidNext(message.metadata.kind)) {
throw new Error(
`Could not add message (${message.metadata.id}) to exchange because ${message.metadata.kind} ` +
Expand All @@ -61,8 +70,8 @@ export class Exchange {

if (this.exchangeId !== undefined && message.metadata.exchangeId !== this.exchangeId) {
throw new Error(
`Could not add message with id ${message.metadata.id} to exchange because it does not have matching ` +
`exchange id ${this.exchangeId}`
`Could not add message (${message.metadata.id}) with exchange id ${message.metadata.exchangeId} to exchange because it does not have matching ` +
`exchange id ${this.exchangeId} as the exchange`
)
}

Expand Down Expand Up @@ -110,6 +119,13 @@ export class Exchange {
return this.rfq?.metadata?.exchangeId
}

/**
* The protocol version of all messages in the Exchange
*/
get protocol(): string | undefined {
return this.rfq?.metadata?.protocol
}

/**
* A sorted list of messages currently in the exchange.
*/
Expand Down
5 changes: 3 additions & 2 deletions packages/protocol/src/message-kinds/close.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Parser } from '../parser.js'
*/
export type CreateCloseOptions = {
data: CloseData
metadata: Omit<CloseMetadata, 'id' | 'kind' | 'createdAt'>
metadata: Omit<CloseMetadata, 'id' | 'kind' | 'createdAt' | 'protocol'> & { protocol?: CloseMetadata['protocol'] }
}

/**
Expand Down Expand Up @@ -60,7 +60,8 @@ export class Close extends Message {
...opts.metadata,
kind : 'close',
id : Message.generateId('close'),
createdAt : new Date().toISOString()
createdAt : new Date().toISOString(),
protocol : opts.metadata.protocol ?? '1.0'
}

const close = new Close(metadata, opts.data)
Expand Down
5 changes: 3 additions & 2 deletions packages/protocol/src/message-kinds/order-status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Parser } from '../parser.js'
*/
export type CreateOrderStatusOptions = {
data: OrderStatusData
metadata: Omit<OrderStatusMetadata, 'id' |'kind' | 'createdAt'>
metadata: Omit<OrderStatusMetadata, 'id' |'kind' | 'createdAt' | 'protocol'> & { protocol?: OrderStatusMetadata['protocol'] }
}

/**
Expand Down Expand Up @@ -61,7 +61,8 @@ export class OrderStatus extends Message {
...opts.metadata,
kind : 'orderstatus',
id : Message.generateId('orderstatus'),
createdAt : new Date().toISOString()
createdAt : new Date().toISOString(),
protocol : opts.metadata.protocol ?? '1.0'
}

const orderStatus = new OrderStatus(metadata, opts.data)
Expand Down
5 changes: 3 additions & 2 deletions packages/protocol/src/message-kinds/order.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Parser } from '../parser.js'
* @beta
*/
export type CreateOrderOptions = {
metadata: Omit<OrderMetadata, 'id' |'kind' | 'createdAt'>
metadata: Omit<OrderMetadata, 'id' |'kind' | 'createdAt' | 'protocol'> & { protocol?: OrderMetadata['protocol'] }
}

/**
Expand Down Expand Up @@ -59,7 +59,8 @@ export class Order extends Message {
...opts.metadata,
kind : 'order',
id : Message.generateId('order'),
createdAt : new Date().toISOString()
createdAt : new Date().toISOString(),
protocol : opts.metadata.protocol ?? '1.0'
}

const order = new Order(metadata, {})
Expand Down
6 changes: 4 additions & 2 deletions packages/protocol/src/message-kinds/quote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Parser } from '../parser.js'
*/
export type CreateQuoteOptions = {
data: QuoteData
metadata: Omit<QuoteMetadata, 'id' |'kind' | 'createdAt'>
metadata: Omit<QuoteMetadata, 'id' |'kind' | 'createdAt' | 'protocol'> & { protocol?: QuoteMetadata['protocol'] }
}

/**
Expand Down Expand Up @@ -64,8 +64,10 @@ export class Quote extends Message {
...opts.metadata,
kind : 'quote',
id : Message.generateId('quote'),
createdAt : new Date().toISOString()
createdAt : new Date().toISOString(),
protocol : opts.metadata.protocol ?? '1.0'
}

const quote = new Quote(metadata, opts.data)
quote.validateData()
return quote
Expand Down
9 changes: 7 additions & 2 deletions packages/protocol/src/message-kinds/rfq.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { Parser } from '../parser.js'
*/
export type CreateRfqOptions = {
data: RfqData
metadata: Omit<RfqMetadata, 'id' | 'kind' | 'createdAt' | 'exchangeId'>
metadata: Omit<RfqMetadata, 'id' | 'kind' | 'createdAt' | 'exchangeId' | 'protocol'> & { protocol?: RfqMetadata['protocol'] }
}

/**
Expand Down Expand Up @@ -68,7 +68,8 @@ export class Rfq extends Message {
kind : 'rfq',
id : id,
exchangeId : id,
createdAt : new Date().toISOString()
createdAt : new Date().toISOString(),
protocol : opts.metadata.protocol ?? '1.0'
}

// TODO: hash and set private fields
Expand All @@ -95,6 +96,10 @@ export class Rfq extends Message {
* @throws if payoutMethod in {@link Rfq.data} property `paymentDetails` cannot be validated against the provided offering's payoutMethod requiredPaymentDetails
*/
async verifyOfferingRequirements(offering: Offering) {
if (offering.metadata.protocol !== this.metadata.protocol) {
throw new Error(`protocol version mismatch. (rfq) ${this.metadata.protocol} !== ${offering.metadata.protocol} (offering)`)
}

if (offering.metadata.id !== this.data.offeringId) {
throw new Error(`offering id mismatch. (rfq) ${this.data.offeringId} !== ${offering.metadata.id} (offering)`)
}
Expand Down
5 changes: 5 additions & 0 deletions packages/protocol/src/message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,11 @@ export abstract class Message {
return this.metadata.externalId
}

/** the protocol version */
get protocol() {
return this.metadata.protocol
}

/** Rfq type guard */
isRfq(): this is Rfq {
return this.metadata.kind === 'rfq'
Expand Down
5 changes: 3 additions & 2 deletions packages/protocol/src/resource-kinds/offering.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Parser } from '../parser.js'
*/
export type CreateOfferingOptions = {
data: OfferingData
metadata: Omit<OfferingMetadata, 'id' |'kind' | 'createdAt' | 'updatedAt'>
metadata: Omit<OfferingMetadata, 'id' |'kind' | 'createdAt' | 'updatedAt' | 'protocol'> & { protocol?: OfferingMetadata['protocol'] }
}

/**
Expand Down Expand Up @@ -59,7 +59,8 @@ export class Offering extends Resource {
...opts.metadata,
kind : 'offering',
id : Resource.generateId('offering'),
createdAt : new Date().toISOString()
createdAt : new Date().toISOString(),
protocol : opts.metadata.protocol ?? '1.0'
}

const offering = new Offering(metadata, opts.data)
Expand Down
10 changes: 7 additions & 3 deletions packages/protocol/src/resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { Crypto } from './crypto.js'
import { validate } from './validator.js'
import { BearerDid } from '@web5/dids'


/**
* tbDEX Resources are published by PFIs for anyone to consume and generally used as a part of the discovery process.
* They are not part of the message exchange, i.e Alice cannot reply to a Resource.
Expand Down Expand Up @@ -42,7 +41,7 @@ export abstract class Resource {
}

/**
* Signs the message as a jws with detached content and sets the signature property
* Signs the resource as a jws with detached content and sets the signature property
* @param did - the signer's DID
* @throws If the signature could not be produced
*/
Expand Down Expand Up @@ -118,7 +117,7 @@ export abstract class Resource {
}

/**
* returns the message as a json object. Automatically used by `JSON.stringify` method.
* returns the resource as a json object. Automatically used by `JSON.stringify` method.
*/
toJSON(): ResourceModel {
return {
Expand Down Expand Up @@ -153,6 +152,11 @@ export abstract class Resource {
return this.metadata.updatedAt
}

/** the protocol version */
get protocol() {
return this.metadata.protocol
}

/** offering type guard */
isOffering(): this is Offering {
return this.metadata.kind === 'offering'
Expand Down
4 changes: 4 additions & 0 deletions packages/protocol/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ export type ResourceMetadata = {
createdAt: string
/** When the resource was last updated. Expressed as ISO8601 */
updatedAt?: string
/** Version of the protocol in use (x.x format). Any exchanges based off this resource must use the same version. */
protocol: `${number}`
}

/**
Expand Down Expand Up @@ -130,6 +132,8 @@ export type MessageMetadata = {
createdAt: string
/** Arbitrary ID for the caller to associate with the message. Optional */
externalId?: string
/** Version of the protocol in use (x.x format). Must be consistent with all other messages in a given exchange */
protocol: `${number}`
}

/**
Expand Down
10 changes: 10 additions & 0 deletions packages/protocol/tests/close.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,15 @@ describe('Close', () => {
expect(closeMessage.exchangeId).to.equal(exchangeId)
expect(closeMessage.externalId).to.equal('ext_1234')
})

it('sets `protocol` to current package version', () => {
const exchangeId = Message.generateId('rfq')
const closeMessage = Close.create({
metadata : { from: 'did:ex:alice', to: 'did:ex:pfi', exchangeId, externalId: 'ext_1234' },
data : { reason: 'beepboop' }
})

expect(closeMessage.protocol).to.equal('1.0')
})
})
})
65 changes: 58 additions & 7 deletions packages/protocol/tests/exchange.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ describe('Exchange', () => {
rfq = Rfq.create({
metadata: {
from : aliceDid.uri,
to : pfiDid.uri,
to : pfiDid.uri
},
data: await DevTools.createRfqData()
})
Expand All @@ -29,7 +29,7 @@ describe('Exchange', () => {
metadata: {
from : aliceDid.uri,
to : pfiDid.uri,
exchangeId : rfq.metadata.exchangeId,
exchangeId : rfq.metadata.exchangeId
},
data: {
reason: 'I dont like u anymore'
Expand All @@ -51,7 +51,7 @@ describe('Exchange', () => {
metadata: {
from : pfiDid.uri,
to : aliceDid.uri,
exchangeId : rfq.metadata.exchangeId,
exchangeId : rfq.metadata.exchangeId
},
data: {
reason: 'I dont like u anymore'
Expand All @@ -72,7 +72,7 @@ describe('Exchange', () => {
metadata: {
from : pfiDid.uri,
to : aliceDid.uri,
exchangeId : rfq.metadata.exchangeId,
exchangeId : rfq.metadata.exchangeId
},
data: {
orderStatus: 'Done'
Expand Down Expand Up @@ -137,7 +137,7 @@ describe('Exchange', () => {
metadata: {
from : aliceDid.uri,
to : pfiDid.uri,
exchangeId : rfq.metadata.exchangeId,
exchangeId : rfq.metadata.exchangeId
},
data: {
reason: 'I dont like u anymore'
Expand Down Expand Up @@ -280,6 +280,57 @@ describe('Exchange', () => {
}
}
})

it('cannot add a message if the protocol versions of the new message and the exchange mismatch', async () => {
const exchange = new Exchange()
exchange.addNextMessage(rfq)


let quote = Quote.create({
metadata: {
from : pfiDid.uri,
to : aliceDid.uri,
exchangeId : rfq.metadata.exchangeId,
protocol : '2.0'
},
data: DevTools.createQuoteData()
})
await quote.sign(pfiDid)

try {
exchange.addNextMessage(quote)
expect.fail()
} catch (e) {
expect(e.message).to.contain('does not have matching protocol version')
expect(e.message).to.contain(rfq.metadata.protocol)
expect(e.message).to.contain('1.0')
}
})

it('cannot add a message if the exchangeId of the new message and the exchange mismatch', async () => {
const exchange = new Exchange()
exchange.addNextMessage(rfq)

let quote = Quote.create({
metadata: {
from : pfiDid.uri,
to : aliceDid.uri,
exchangeId : '123',

},
data: DevTools.createQuoteData()
})
await quote.sign(pfiDid)

try {
exchange.addNextMessage(quote)
expect.fail()
} catch (e) {
expect(e.message).to.contain('does not have matching exchange id')
expect(e.message).to.contain(rfq.metadata.exchangeId)
expect(e.message).to.contain('123')
}
})
})
})

Expand All @@ -289,7 +340,7 @@ describe('Exchange', () => {
const rfq = Rfq.create({
metadata: {
from : aliceDid.uri,
to : pfiDid.uri,
to : pfiDid.uri
},
data: await DevTools.createRfqData()
})
Expand Down Expand Up @@ -318,7 +369,7 @@ describe('Exchange', () => {
metadata: {
from : pfiDid.uri,
to : aliceDid.uri,
exchangeId : rfq.metadata.exchangeId,
exchangeId : rfq.metadata.exchangeId
},
data: {
orderStatus: 'Done'
Expand Down
Loading

0 comments on commit 46481e7

Please sign in to comment.