From 5031c8ca74fefce069f3d84ef978900ecb0c359d Mon Sep 17 00:00:00 2001
From: Pavel <14926950+prichodko@users.noreply.github.com>
Date: Tue, 14 Jun 2022 14:28:08 +0200
Subject: [PATCH] Use chat keys in the UI (#274)
* add util for compressing public key
* add chat keys to account and member
* use compressPublicKey in color hash util
* use chat keys in UI
---
packages/status-js/src/account.ts | 5 ++--
packages/status-js/src/client/member.ts | 6 ++--
.../src/utils/compress-public-key.test.ts | 30 +++++++++++++++++++
.../src/utils/compress-public-key.ts | 10 +++++++
.../src/utils/public-key-to-color-hash.ts | 16 ++++------
.../member-sidebar/disconnect-dialog.tsx | 2 +-
.../components/member-sidebar/member-item.tsx | 4 +--
.../components/member-sidebar/user-item.tsx | 2 +-
.../components/user-profile-dialog/index.tsx | 16 ++++++----
9 files changed, 65 insertions(+), 26 deletions(-)
create mode 100644 packages/status-js/src/utils/compress-public-key.test.ts
create mode 100644 packages/status-js/src/utils/compress-public-key.ts
diff --git a/packages/status-js/src/account.ts b/packages/status-js/src/account.ts
index bd18a02e..399ef6c5 100644
--- a/packages/status-js/src/account.ts
+++ b/packages/status-js/src/account.ts
@@ -2,6 +2,7 @@ import { keccak256 } from 'ethereum-cryptography/keccak'
import { getPublicKey, sign, utils } from 'ethereum-cryptography/secp256k1'
import { bytesToHex, concatBytes } from 'ethereum-cryptography/utils'
+import { compressPublicKey } from './utils/compress-public-key'
import { generateUsername } from './utils/generate-username'
export class Account {
@@ -13,12 +14,10 @@ export class Account {
constructor() {
const privateKey = utils.randomPrivateKey()
const publicKey = getPublicKey(privateKey)
- const chatKey = getPublicKey(privateKey, true)
this.privateKey = bytesToHex(privateKey)
this.publicKey = bytesToHex(publicKey)
- // TODO?: add 0x prefix to public key
- this.chatKey = bytesToHex(chatKey)
+ this.chatKey = '0x' + compressPublicKey(this.publicKey)
this.username = generateUsername('0x' + this.publicKey)
}
diff --git a/packages/status-js/src/client/member.ts b/packages/status-js/src/client/member.ts
index 7406da25..c9f13635 100644
--- a/packages/status-js/src/client/member.ts
+++ b/packages/status-js/src/client/member.ts
@@ -1,3 +1,4 @@
+import { compressPublicKey } from '../utils/compress-public-key'
import { generateUsername } from '../utils/generate-username'
import { publicKeyToColorHash } from '../utils/public-key-to-color-hash'
@@ -5,13 +6,14 @@ import type { ColorHash } from '../utils/public-key-to-color-hash'
export class Member {
publicKey: string
+ chatKey: string
username: string
colorHash: ColorHash
constructor(publicKey: string) {
this.publicKey = publicKey
+ this.chatKey = '0x' + compressPublicKey(publicKey)
this.username = generateUsername(publicKey)
- // TODO: can it fail?
- this.colorHash = publicKeyToColorHash(publicKey)!
+ this.colorHash = publicKeyToColorHash(publicKey)
}
}
diff --git a/packages/status-js/src/utils/compress-public-key.test.ts b/packages/status-js/src/utils/compress-public-key.test.ts
new file mode 100644
index 00000000..a5efa699
--- /dev/null
+++ b/packages/status-js/src/utils/compress-public-key.test.ts
@@ -0,0 +1,30 @@
+import { getPublicKey, utils } from 'ethereum-cryptography/secp256k1'
+import { bytesToHex } from 'ethereum-cryptography/utils'
+
+import { compressPublicKey } from './compress-public-key'
+
+describe('compressPublicKey', () => {
+ it('should return compressed public key', () => {
+ const privateKey = utils.randomPrivateKey()
+
+ const publicKey = bytesToHex(getPublicKey(privateKey))
+ const compressedPublicKey = bytesToHex(getPublicKey(privateKey, true))
+
+ expect(compressPublicKey(publicKey)).toEqual(compressedPublicKey)
+ })
+
+ it('should accept public key with a base prefix', () => {
+ const privateKey = utils.randomPrivateKey()
+
+ const publicKey = '0x' + bytesToHex(getPublicKey(privateKey))
+ const compressedPublicKey = bytesToHex(getPublicKey(privateKey, true))
+
+ expect(compressPublicKey(publicKey)).toEqual(compressedPublicKey)
+ })
+
+ it('should throw error if public key is not a valid hex', () => {
+ expect(() => {
+ compressPublicKey('not a valid public key')
+ }).toThrowErrorMatchingInlineSnapshot(`"Invalid public key"`)
+ })
+})
diff --git a/packages/status-js/src/utils/compress-public-key.ts b/packages/status-js/src/utils/compress-public-key.ts
new file mode 100644
index 00000000..9906b859
--- /dev/null
+++ b/packages/status-js/src/utils/compress-public-key.ts
@@ -0,0 +1,10 @@
+import * as secp from 'ethereum-cryptography/secp256k1'
+
+export function compressPublicKey(publicKey: string): string {
+ try {
+ const pk = publicKey.replace(/^0[xX]/, '') // ensures hexadecimal digits without "base prefix"
+ return secp.Point.fromHex(pk).toHex(true)
+ } catch (error) {
+ throw new Error('Invalid public key')
+ }
+}
diff --git a/packages/status-js/src/utils/public-key-to-color-hash.ts b/packages/status-js/src/utils/public-key-to-color-hash.ts
index 2b162ac3..41d30249 100644
--- a/packages/status-js/src/utils/public-key-to-color-hash.ts
+++ b/packages/status-js/src/utils/public-key-to-color-hash.ts
@@ -1,22 +1,16 @@
import * as secp256k1 from 'ethereum-cryptography/secp256k1'
+import { compressPublicKey } from './compress-public-key'
+
export type ColorHash = number[][]
const COLOR_HASH_COLORS_COUNT = 32
const COLOR_HASH_SEGMENT_MAX_LENGTH = 5
-export function publicKeyToColorHash(publicKey: string): ColorHash | undefined {
- const publicKeyHex = publicKey.replace(/^0[xX]/, '') // ensures hexadecimal digits without "base prefix"
+export function publicKeyToColorHash(publicKey: string): ColorHash {
+ const compressedPublicKey = compressPublicKey(publicKey)
- let compressedPublicKeyDigits: string
- try {
- compressedPublicKeyDigits =
- secp256k1.Point.fromHex(publicKeyHex).toHex(true) // validates and adds "sign prefix" too
- } catch (error) {
- return undefined
- }
-
- const colorHashHex = compressedPublicKeyDigits.slice(43, 63)
+ const colorHashHex = compressedPublicKey.slice(43, 63)
const colorHash = hexToColorHash(
colorHashHex,
COLOR_HASH_COLORS_COUNT,
diff --git a/packages/status-react/src/components/member-sidebar/disconnect-dialog.tsx b/packages/status-react/src/components/member-sidebar/disconnect-dialog.tsx
index 7b167e9c..c29ac6b4 100644
--- a/packages/status-react/src/components/member-sidebar/disconnect-dialog.tsx
+++ b/packages/status-react/src/components/member-sidebar/disconnect-dialog.tsx
@@ -20,7 +20,7 @@ export const DisconnectDialog = (props: Props) => {
{account.username}
- Chatkey: {account.publicKey}
+ Chatkey: {account.chatKey}
diff --git a/packages/status-react/src/components/member-sidebar/member-item.tsx b/packages/status-react/src/components/member-sidebar/member-item.tsx
index a665ad6d..5b912857 100644
--- a/packages/status-react/src/components/member-sidebar/member-item.tsx
+++ b/packages/status-react/src/components/member-sidebar/member-item.tsx
@@ -14,7 +14,7 @@ interface Props {
export const MemberItem = (props: Props) => {
const { member, indicator, verified, untrustworthy } = props
- const { publicKey, username, colorHash } = member
+ const { chatKey, username, colorHash } = member
return (
@@ -65,7 +65,7 @@ export const MemberItem = (props: Props) => {
)}
- {publicKey}
+ {chatKey}
diff --git a/packages/status-react/src/components/member-sidebar/user-item.tsx b/packages/status-react/src/components/member-sidebar/user-item.tsx
index a6e0d9a3..a432445e 100644
--- a/packages/status-react/src/components/member-sidebar/user-item.tsx
+++ b/packages/status-react/src/components/member-sidebar/user-item.tsx
@@ -25,7 +25,7 @@ export const UserItem = (props: Props) => {
- {account.publicKey}
+ {account.chatKey}
diff --git a/packages/status-react/src/components/user-profile-dialog/index.tsx b/packages/status-react/src/components/user-profile-dialog/index.tsx
index 466e806e..fc9446f6 100644
--- a/packages/status-react/src/components/user-profile-dialog/index.tsx
+++ b/packages/status-react/src/components/user-profile-dialog/index.tsx
@@ -2,20 +2,24 @@ import React from 'react'
import { Avatar, Dialog, EmojiHash, Heading, Text } from '~/src/system'
+import type { Member } from '~/src/protocol'
+
interface Props {
- name: string
+ member: Member
}
// TODO: Add all states of contact, wait for desktop release
export const UserProfileDialog = (props: Props) => {
- const { name, ...dialogProps } = props
+ const { member, ...dialogProps } = props
+
+ const { username, colorHash, chatKey } = member
return (
-