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

Add protocol version #191

Merged
merged 24 commits into from
Mar 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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'] }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking: I kinda hate indexed access type syntax in typescript. Instead I prefer to just use the underlying type, in this case,

`${number}`
Suggested change
metadata: Omit<CloseMetadata, 'id' | 'kind' | 'createdAt' | 'protocol'> & { protocol?: CloseMetadata['protocol'] }
metadata: Omit<CloseMetadata, 'id' | 'kind' | 'createdAt' | 'protocol'> & { protocol?: `${number}` }

Up to you if you feel like doing this. Just a personal preference on my part.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious what you don't like about it! the way it looks, or is there something about the way it works that you dislike?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's a readability thing. it's an abstraction that i personally don't find helpful. i don't like needing to have to look at a separate file or type to figure out what i'm actually working with, especially if it's as simple as a number or string.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh got it. I'm leaning towards leaving it as is because theoretically, the types could drift between CloseMetadata and this field if it's not referencing CloseMetadata.

}

/**
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 { 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 @@
}

/**
* 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 @@
}

/**
* 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 @@
return this.metadata.updatedAt
}

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

Check warning on line 158 in packages/protocol/src/resource.ts

View check run for this annotation

Codecov / codecov/patch

packages/protocol/src/resource.ts#L157-L158

Added lines #L157 - L158 were not covered by tests

/** 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}`
diehuxx marked this conversation as resolved.
Show resolved Hide resolved
}

/**
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
Loading