-
Notifications
You must be signed in to change notification settings - Fork 43
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: simplify contact bundle api (#182)
* feat: simplify contact bundle api * fix: encode bundle correctly
- Loading branch information
Showing
3 changed files
with
35 additions
and
88 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,73 +1,39 @@ | ||
import { contact, publicKey } from '@xmtp/proto' | ||
import { PublicKeyBundle, SignedPublicKeyBundle } from './crypto' | ||
|
||
// ContactBundle packages all the information which a client uses to advertise on the network. | ||
// V1 uses the legacy PublicKeyBundle. | ||
export class ContactBundleV1 implements contact.ContactBundleV1 { | ||
keyBundle: PublicKeyBundle | ||
|
||
constructor(bundle: contact.ContactBundleV1) { | ||
if (!bundle.keyBundle) { | ||
throw new Error('missing keyBundle') | ||
} | ||
this.keyBundle = new PublicKeyBundle(bundle.keyBundle) | ||
} | ||
|
||
toBytes(): Uint8Array { | ||
return contact.ContactBundle.encode({ | ||
v1: { | ||
keyBundle: this.keyBundle, | ||
}, | ||
v2: undefined, | ||
}).finish() | ||
} | ||
} | ||
|
||
// ContactBundle packages all the information which a client uses to advertise on the network. | ||
// V2 uses the SignedPublicKeyBundle. | ||
export class ContactBundleV2 implements contact.ContactBundleV2 { | ||
keyBundle: SignedPublicKeyBundle | ||
|
||
constructor(bundle: contact.ContactBundleV2) { | ||
if (!bundle.keyBundle) { | ||
throw new Error('missing keyBundle') | ||
} | ||
this.keyBundle = new SignedPublicKeyBundle(bundle.keyBundle) | ||
} | ||
|
||
toBytes(): Uint8Array { | ||
return contact.ContactBundle.encode({ | ||
v1: undefined, | ||
v2: { | ||
keyBundle: this.keyBundle, | ||
}, | ||
}).finish() | ||
} | ||
|
||
static fromLegacyBundle(bundle: ContactBundleV1): ContactBundleV2 { | ||
return new ContactBundleV2({ | ||
keyBundle: SignedPublicKeyBundle.fromLegacyBundle(bundle.keyBundle), | ||
}) | ||
} | ||
} | ||
|
||
// This is the union of all supported bundle versions. | ||
export type ContactBundle = ContactBundleV1 | ContactBundleV2 | ||
|
||
// This is the primary function for reading contact bundles off the wire. | ||
export function decodeContactBundle(bytes: Uint8Array): ContactBundle { | ||
// Decodes contact bundles from the contact topic. | ||
export function decodeContactBundle( | ||
bytes: Uint8Array | ||
): PublicKeyBundle | SignedPublicKeyBundle { | ||
let cb: contact.ContactBundle | ||
try { | ||
cb = contact.ContactBundle.decode(bytes) | ||
} catch (e) { | ||
const pb = publicKey.PublicKeyBundle.decode(bytes) | ||
cb = { v1: { keyBundle: new PublicKeyBundle(pb) }, v2: undefined } | ||
} | ||
if (cb.v1) { | ||
return new ContactBundleV1(cb.v1) | ||
if (cb.v1?.keyBundle) { | ||
return new PublicKeyBundle(cb.v1.keyBundle) | ||
} | ||
if (cb.v2) { | ||
return new ContactBundleV2(cb.v2) | ||
if (cb.v2?.keyBundle) { | ||
return new SignedPublicKeyBundle(cb.v2.keyBundle) | ||
} | ||
throw new Error('unknown or invalid contact bundle') | ||
} | ||
|
||
// Encodes public key bundle for the contact topic. | ||
export function encodeContactBundle( | ||
bundle: PublicKeyBundle | SignedPublicKeyBundle | ||
): Uint8Array { | ||
if (bundle instanceof PublicKeyBundle) { | ||
return contact.ContactBundle.encode({ | ||
v1: { keyBundle: bundle }, | ||
v2: undefined, | ||
}).finish() | ||
} else { | ||
return contact.ContactBundle.encode({ | ||
v1: undefined, | ||
v2: { keyBundle: bundle }, | ||
}).finish() | ||
} | ||
throw new Error('unknown contact bundle version') | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,47 +1,29 @@ | ||
import * as assert from 'assert' | ||
import { | ||
ContactBundleV1, | ||
ContactBundleV2, | ||
decodeContactBundle, | ||
} from '../src/ContactBundle' | ||
import { decodeContactBundle, encodeContactBundle } from '../src/ContactBundle' | ||
import { | ||
PrivateKeyBundleV1, | ||
PrivateKeyBundleV2, | ||
PublicKeyBundle, | ||
SignedPublicKeyBundle, | ||
PrivateKey, | ||
} from '../src' | ||
import { newWallet } from './helpers' | ||
|
||
describe('ContactBundles', function () { | ||
it('roundtrip', async function () { | ||
const priv = await PrivateKeyBundleV1.generate() | ||
const pub = priv.getPublicKeyBundle() | ||
let bytes = pub.toBytes() | ||
let bytes = encodeContactBundle(pub) | ||
const cb = decodeContactBundle(bytes) | ||
expect(cb.keyBundle).toBeInstanceOf(PublicKeyBundle) | ||
assert.ok(pub.equals(cb.keyBundle as PublicKeyBundle)) | ||
|
||
const cb1 = new ContactBundleV1({ keyBundle: priv.getPublicKeyBundle() }) | ||
bytes = cb1.toBytes() | ||
const cb2 = decodeContactBundle(bytes) | ||
expect(cb2.keyBundle).toBeInstanceOf(PublicKeyBundle) | ||
assert.ok(pub.equals(cb2.keyBundle as PublicKeyBundle)) | ||
|
||
const bytes2 = cb2.toBytes() | ||
assert.deepEqual(bytes, bytes2) | ||
expect(cb).toBeInstanceOf(PublicKeyBundle) | ||
assert.ok(pub.equals(cb as PublicKeyBundle)) | ||
}) | ||
it('roundtrip v2', async function () { | ||
const wallet = newWallet() | ||
const priv = await PrivateKeyBundleV2.generate(wallet) | ||
const pub = priv.getPublicKeyBundle() | ||
const cb1 = new ContactBundleV2({ keyBundle: priv.getPublicKeyBundle() }) | ||
let bytes = cb1.toBytes() | ||
const cb2 = decodeContactBundle(bytes) | ||
expect(cb2.keyBundle).toBeInstanceOf(SignedPublicKeyBundle) | ||
assert.ok(pub.equals(cb2.keyBundle as SignedPublicKeyBundle)) | ||
|
||
const bytes2 = cb2.toBytes() | ||
assert.deepEqual(bytes, bytes2) | ||
let bytes = encodeContactBundle(pub) | ||
const cb = decodeContactBundle(bytes) | ||
expect(cb).toBeInstanceOf(SignedPublicKeyBundle) | ||
assert.ok(pub.equals(cb as SignedPublicKeyBundle)) | ||
}) | ||
}) |