mirror of
https://github.com/orbitdb/orbitdb.git
synced 2025-03-30 15:08:28 +00:00
feat: Use composite storage for key store.
This commit is contained in:
parent
0c5c44e429
commit
ba71a7985e
@ -1,6 +1,7 @@
|
||||
import * as crypto from '@libp2p/crypto'
|
||||
import secp256k1 from 'secp256k1'
|
||||
import { Buffer } from 'safe-buffer'
|
||||
import ComposedStorage from './storage/composed.js'
|
||||
import LevelStorage from './storage/level.js'
|
||||
import LRUStorage from './storage/lru.js'
|
||||
|
||||
@ -51,22 +52,25 @@ const signMessage = async (key, data) => {
|
||||
return Buffer.from(await key.sign(data)).toString('hex')
|
||||
}
|
||||
|
||||
const verifiedCache = await LRUStorage({ size: 1000 })
|
||||
|
||||
const verifyMessage = async (signature, publicKey, data) => {
|
||||
// const cached = verifiedCache.get(signature)
|
||||
const cached = null
|
||||
const cached = await verifiedCache.get(signature)
|
||||
|
||||
let res = false
|
||||
|
||||
if (!cached) {
|
||||
const verified = await verifySignature(signature, publicKey, data)
|
||||
res = verified
|
||||
// if (verified) {
|
||||
// verifiedCache.set(signature, { publicKey, data })
|
||||
// }
|
||||
if (verified) {
|
||||
await verifiedCache.put(signature, { publicKey, data })
|
||||
}
|
||||
} else {
|
||||
const compare = (cached, data) => {
|
||||
// let match
|
||||
// if (v === 'v0') {
|
||||
// match = Buffer.compare(Buffer.alloc(30, cached), Buffer.alloc(30, data)) === 0
|
||||
// } else {
|
||||
/* let match
|
||||
if (v === 'v0') {
|
||||
match = Buffer.compare(Buffer.alloc(30, cached), Buffer.alloc(30, data)) === 0
|
||||
} else { */
|
||||
const match = Buffer.isBuffer(data) ? Buffer.compare(cached, data) === 0 : cached === data
|
||||
// }
|
||||
return match
|
||||
@ -78,9 +82,8 @@ const verifyMessage = async (signature, publicKey, data) => {
|
||||
|
||||
// const verifiedCache = new LRU(1000)
|
||||
|
||||
const KeyStore = async ({ storage, cache } = {}) => {
|
||||
storage = storage || await LevelStorage('./keystore')
|
||||
cache = cache || await LRUStorage({ size: 1000 })
|
||||
const KeyStore = async ({ storage } = {}) => {
|
||||
storage = storage || await ComposedStorage(LevelStorage('./keystore'), LRUStorage({ size: 1000 }))
|
||||
|
||||
const close = async () => {
|
||||
if (!storage) return
|
||||
@ -90,7 +93,6 @@ const KeyStore = async ({ storage, cache } = {}) => {
|
||||
const clear = async () => {
|
||||
if (!storage) return
|
||||
await storage.clear()
|
||||
await cache.clear()
|
||||
}
|
||||
|
||||
const hasKey = async (id) => {
|
||||
@ -103,7 +105,7 @@ const KeyStore = async ({ storage, cache } = {}) => {
|
||||
|
||||
let hasKey = false
|
||||
try {
|
||||
const storedKey = await cache.get(id) || await storage.get(id)
|
||||
const storedKey = await storage.get(id)
|
||||
hasKey = storedKey !== undefined && storedKey !== null
|
||||
} catch (e) {
|
||||
// Catches 'Error: ENOENT: no such file or directory, open <path>'
|
||||
@ -119,7 +121,6 @@ const KeyStore = async ({ storage, cache } = {}) => {
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
cache.put(id, key)
|
||||
}
|
||||
|
||||
const createKey = async (id, { entropy } = {}) => {
|
||||
@ -148,8 +149,6 @@ const KeyStore = async ({ storage, cache } = {}) => {
|
||||
console.log(e)
|
||||
}
|
||||
|
||||
cache.put(id, key)
|
||||
|
||||
return keys
|
||||
}
|
||||
|
||||
@ -157,14 +156,14 @@ const KeyStore = async ({ storage, cache } = {}) => {
|
||||
if (!id) {
|
||||
throw new Error('id needed to get a key')
|
||||
}
|
||||
|
||||
if (storage.status && storage.status !== 'open') {
|
||||
return null
|
||||
}
|
||||
|
||||
const cachedKey = await cache.get(id)
|
||||
let storedKey
|
||||
try {
|
||||
storedKey = cachedKey || await storage.get(id)
|
||||
storedKey = await storage.get(id)
|
||||
} catch (e) {
|
||||
// ignore ENOENT error
|
||||
}
|
||||
@ -173,15 +172,11 @@ const KeyStore = async ({ storage, cache } = {}) => {
|
||||
return
|
||||
}
|
||||
|
||||
const deserializedKey = cachedKey || JSON.parse(storedKey)
|
||||
const deserializedKey = JSON.parse(storedKey)
|
||||
if (!deserializedKey) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!cachedKey) {
|
||||
cache.put(id, deserializedKey)
|
||||
}
|
||||
|
||||
return unmarshal(Buffer.from(deserializedKey.privateKey, 'hex'))
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
import { strictEqual, deepStrictEqual } from 'assert'
|
||||
import LevelStorage from '../src/storage/level.js'
|
||||
import LRUStorage from '../src/storage/lru.js'
|
||||
import ComposedStorage from '../src/storage/composed.js'
|
||||
import KeyStore, { signMessage, verifyMessage } from '../src/key-store.js'
|
||||
import { testAPIs } from 'orbit-db-test-utils'
|
||||
import path from 'path'
|
||||
@ -9,254 +11,270 @@ import { signingKeys } from './fixtures/orbit-db-identity-keys.js'
|
||||
|
||||
Object.keys(testAPIs).forEach((IPFS) => {
|
||||
describe('KeyStore (' + IPFS + ')', () => {
|
||||
const fixturePath = path.join('test', 'fixtures', 'keys', 'signing-keys')
|
||||
const storagePath = path.join('test', 'keys', 'signing-keys')
|
||||
let keystore
|
||||
|
||||
beforeEach(async () => {
|
||||
await fs.copy(fixturePath, storagePath)
|
||||
describe('Creating and retrieving keys', () => {
|
||||
beforeEach(async () => {
|
||||
keystore = await KeyStore()
|
||||
})
|
||||
|
||||
const storage = await LevelStorage({ path: storagePath })
|
||||
afterEach(async () => {
|
||||
await keystore.clear()
|
||||
await keystore.close()
|
||||
})
|
||||
|
||||
keystore = await KeyStore({ storage })
|
||||
})
|
||||
it('creates a key', async () => {
|
||||
const id = 'key1'
|
||||
await keystore.createKey(id)
|
||||
const hasKey = await keystore.hasKey(id)
|
||||
strictEqual(hasKey, true)
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
await keystore.clear()
|
||||
await keystore.close()
|
||||
it('throws an error when creating a key without an id', async () => {
|
||||
let err
|
||||
|
||||
rmrf.sync(path.join('test', 'keys'))
|
||||
})
|
||||
try {
|
||||
await keystore.createKey()
|
||||
} catch (e) {
|
||||
err = e.toString()
|
||||
}
|
||||
|
||||
it('creates a key', async () => {
|
||||
const id = 'key1'
|
||||
await keystore.createKey(id)
|
||||
const hasKey = await keystore.hasKey(id)
|
||||
strictEqual(hasKey, true)
|
||||
})
|
||||
strictEqual(err, 'Error: id needed to create a key')
|
||||
})
|
||||
|
||||
it('throws an error when creating a key without an id', async () => {
|
||||
let err
|
||||
it('throws an error when creating a key with a null id', async () => {
|
||||
let err
|
||||
|
||||
try {
|
||||
await keystore.createKey()
|
||||
} catch (e) {
|
||||
err = e.toString()
|
||||
}
|
||||
try {
|
||||
await keystore.createKey(null)
|
||||
} catch (e) {
|
||||
err = e.toString()
|
||||
}
|
||||
|
||||
strictEqual(err, 'Error: id needed to create a key')
|
||||
})
|
||||
strictEqual(err, 'Error: id needed to create a key')
|
||||
})
|
||||
|
||||
it('throws an error when creating a key with a null id', async () => {
|
||||
let err
|
||||
it('returns true if key exists', async () => {
|
||||
const id = 'key1'
|
||||
|
||||
try {
|
||||
await keystore.createKey(null)
|
||||
} catch (e) {
|
||||
err = e.toString()
|
||||
}
|
||||
await keystore.createKey(id)
|
||||
const hasKey = await keystore.hasKey(id)
|
||||
strictEqual(hasKey, true)
|
||||
})
|
||||
|
||||
strictEqual(err, 'Error: id needed to create a key')
|
||||
})
|
||||
it('returns false if key does not exist', async () => {
|
||||
const id = 'key1'
|
||||
const hasKey = await keystore.hasKey(id)
|
||||
strictEqual(hasKey, false)
|
||||
})
|
||||
|
||||
it('returns true if key exists', async () => {
|
||||
const id = 'key1'
|
||||
it('throws an error when checking if key exists when no id is specified', async () => {
|
||||
let err
|
||||
try {
|
||||
await keystore.hasKey()
|
||||
} catch (e) {
|
||||
err = e.toString()
|
||||
}
|
||||
strictEqual(err, 'Error: id needed to check a key')
|
||||
})
|
||||
|
||||
await keystore.createKey(id)
|
||||
const hasKey = await keystore.hasKey(id)
|
||||
strictEqual(hasKey, true)
|
||||
})
|
||||
it('gets a key', async () => {
|
||||
const id = 'key1'
|
||||
const keys = await keystore.createKey(id)
|
||||
deepStrictEqual(await keystore.getKey(id), keys)
|
||||
})
|
||||
|
||||
it('returns false if key does not exist', async () => {
|
||||
const id = 'key1'
|
||||
const hasKey = await keystore.hasKey(id)
|
||||
strictEqual(hasKey, false)
|
||||
})
|
||||
it('throws an error when getting a key without an id', async () => {
|
||||
const id = 'key1'
|
||||
let err
|
||||
|
||||
it('throws an error when checking if key exists when no id is specified', async () => {
|
||||
let err
|
||||
try {
|
||||
await keystore.hasKey()
|
||||
} catch (e) {
|
||||
err = e.toString()
|
||||
}
|
||||
strictEqual(err, 'Error: id needed to check a key')
|
||||
})
|
||||
await keystore.createKey(id)
|
||||
|
||||
it('gets a key', async () => {
|
||||
const id = 'key1'
|
||||
const keys = await keystore.createKey(id)
|
||||
deepStrictEqual(await keystore.getKey(id), keys)
|
||||
})
|
||||
try {
|
||||
await keystore.getKey()
|
||||
} catch (e) {
|
||||
err = e.toString()
|
||||
}
|
||||
|
||||
it('throws an error when getting a key without an id', async () => {
|
||||
const id = 'key1'
|
||||
let err
|
||||
strictEqual(err, 'Error: id needed to get a key')
|
||||
})
|
||||
|
||||
await keystore.createKey(id)
|
||||
it('throws an error when getting a key with a null id', async () => {
|
||||
const id = 'key1'
|
||||
let err
|
||||
|
||||
try {
|
||||
await keystore.getKey()
|
||||
} catch (e) {
|
||||
err = e.toString()
|
||||
}
|
||||
await keystore.createKey(id)
|
||||
|
||||
strictEqual(err, 'Error: id needed to get a key')
|
||||
})
|
||||
try {
|
||||
await keystore.getKey(null)
|
||||
} catch (e) {
|
||||
err = e.toString()
|
||||
}
|
||||
|
||||
it('throws an error when getting a key with a null id', async () => {
|
||||
const id = 'key1'
|
||||
let err
|
||||
strictEqual(err, 'Error: id needed to get a key')
|
||||
})
|
||||
|
||||
await keystore.createKey(id)
|
||||
it('gets a non-existent key', async () => {
|
||||
const expected = undefined
|
||||
const id = 'key1'
|
||||
|
||||
try {
|
||||
await keystore.getKey(null)
|
||||
} catch (e) {
|
||||
err = e.toString()
|
||||
}
|
||||
const actual = await keystore.getKey(id)
|
||||
|
||||
strictEqual(err, 'Error: id needed to get a key')
|
||||
})
|
||||
|
||||
it('gets a non-existent key', async () => {
|
||||
const expected = undefined
|
||||
const id = 'key1'
|
||||
|
||||
const actual = await keystore.getKey(id)
|
||||
|
||||
strictEqual(actual, expected)
|
||||
})
|
||||
|
||||
describe('signing', () => {
|
||||
it('signs data', async () => {
|
||||
const expected = '304402207eb6e4f4b2c56665c505696c41ec0831c6c2998620589d4b6f405d49134dea5102207e71ba37d94b7a70e3d9fb3bea7c8d8b7082c3c880b6831e9613a0a3e7aabd9f'
|
||||
|
||||
const key = await keystore.getKey('userA')
|
||||
const actual = await signMessage(key, 'data data data')
|
||||
strictEqual(actual, expected)
|
||||
})
|
||||
|
||||
it('throws an error if no key is passed', async () => {
|
||||
let err
|
||||
try {
|
||||
await signMessage(null, 'data data data')
|
||||
} catch (e) {
|
||||
err = e.toString()
|
||||
}
|
||||
|
||||
strictEqual(err, 'Error: No signing key given')
|
||||
})
|
||||
|
||||
it('throws an error if no data is passed', async () => {
|
||||
const key = 'key_1'
|
||||
let err
|
||||
try {
|
||||
await signMessage(key)
|
||||
} catch (e) {
|
||||
err = e.toString()
|
||||
}
|
||||
|
||||
strictEqual(err, 'Error: Given input data was undefined')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Getting the public key', async () => {
|
||||
let key
|
||||
describe('Using keys for signing and verifying', () => {
|
||||
const fixturePath = path.join('test', 'fixtures', 'keys', 'signing-keys')
|
||||
const storagePath = path.join('test', 'keys', 'signing-keys')
|
||||
|
||||
beforeEach(async () => {
|
||||
key = await keystore.getKey('userA')
|
||||
await fs.copy(fixturePath, storagePath)
|
||||
|
||||
// load existing keystore
|
||||
const storage = await LevelStorage({ path: storagePath })
|
||||
const cache = await LRUStorage({ size: 1000 })
|
||||
const composedStorage = await ComposedStorage(storage, cache)
|
||||
keystore = await KeyStore({ storage: composedStorage })
|
||||
})
|
||||
|
||||
it('gets the public key', async () => {
|
||||
const expected = '04e0480538c2a39951d054e17ff31fde487cb1031d0044a037b53ad2e028a3e77c34e864b8579e7c7b24542959e7325361a96f1efb41ed5d3c08f0ea1e5dd0c8ed'
|
||||
const publicKey = await keystore.getPublic(key)
|
||||
strictEqual(publicKey, expected)
|
||||
afterEach(async () => {
|
||||
await keystore.clear()
|
||||
await keystore.close()
|
||||
|
||||
rmrf.sync(path.join('test', 'keys'))
|
||||
})
|
||||
|
||||
it('gets the public key buffer', async () => {
|
||||
const expected = {
|
||||
type: 'Buffer',
|
||||
data: [4, 224, 72, 5, 56, 194, 163, 153, 81, 208, 84,
|
||||
225, 127, 243, 31, 222, 72, 124, 177, 3, 29, 0,
|
||||
68, 160, 55, 181, 58, 210, 224, 40, 163, 231, 124,
|
||||
52, 232, 100, 184, 87, 158, 124, 123, 36, 84, 41,
|
||||
89, 231, 50, 83, 97, 169, 111, 30, 251, 65, 237,
|
||||
93, 60, 8, 240, 234, 30, 93, 208, 200, 237]
|
||||
}
|
||||
const publicKey = await keystore.getPublic(key, { format: 'buffer' })
|
||||
describe('Signing', () => {
|
||||
it('signs data', async () => {
|
||||
const expected = '304402207eb6e4f4b2c56665c505696c41ec0831c6c2998620589d4b6f405d49134dea5102207e71ba37d94b7a70e3d9fb3bea7c8d8b7082c3c880b6831e9613a0a3e7aabd9f'
|
||||
|
||||
deepStrictEqual(publicKey.toJSON(), expected)
|
||||
const key = await keystore.getKey('userA')
|
||||
const actual = await signMessage(key, 'data data data')
|
||||
strictEqual(actual, expected)
|
||||
})
|
||||
|
||||
it('throws an error if no key is passed', async () => {
|
||||
let err
|
||||
try {
|
||||
await signMessage(null, 'data data data')
|
||||
} catch (e) {
|
||||
err = e.toString()
|
||||
}
|
||||
|
||||
strictEqual(err, 'Error: No signing key given')
|
||||
})
|
||||
|
||||
it('throws an error if no data is passed', async () => {
|
||||
const key = 'key_1'
|
||||
let err
|
||||
try {
|
||||
await signMessage(key)
|
||||
} catch (e) {
|
||||
err = e.toString()
|
||||
}
|
||||
|
||||
strictEqual(err, 'Error: Given input data was undefined')
|
||||
})
|
||||
})
|
||||
|
||||
it('gets the public key when decompress is false', async () => {
|
||||
const expectedCompressedKey = signingKeys.userA.publicKey
|
||||
const publicKey = await keystore.getPublic(key, { decompress: false })
|
||||
strictEqual(publicKey, expectedCompressedKey)
|
||||
describe('Getting the public key', async () => {
|
||||
let key
|
||||
|
||||
beforeEach(async () => {
|
||||
key = await keystore.getKey('userA')
|
||||
})
|
||||
|
||||
it('gets the public key', async () => {
|
||||
const expected = '04e0480538c2a39951d054e17ff31fde487cb1031d0044a037b53ad2e028a3e77c34e864b8579e7c7b24542959e7325361a96f1efb41ed5d3c08f0ea1e5dd0c8ed'
|
||||
const publicKey = await keystore.getPublic(key)
|
||||
strictEqual(publicKey, expected)
|
||||
})
|
||||
|
||||
it('gets the public key buffer', async () => {
|
||||
const expected = {
|
||||
type: 'Buffer',
|
||||
data: [4, 224, 72, 5, 56, 194, 163, 153, 81, 208, 84,
|
||||
225, 127, 243, 31, 222, 72, 124, 177, 3, 29, 0,
|
||||
68, 160, 55, 181, 58, 210, 224, 40, 163, 231, 124,
|
||||
52, 232, 100, 184, 87, 158, 124, 123, 36, 84, 41,
|
||||
89, 231, 50, 83, 97, 169, 111, 30, 251, 65, 237,
|
||||
93, 60, 8, 240, 234, 30, 93, 208, 200, 237]
|
||||
}
|
||||
const publicKey = await keystore.getPublic(key, { format: 'buffer' })
|
||||
|
||||
deepStrictEqual(publicKey.toJSON(), expected)
|
||||
})
|
||||
|
||||
it('gets the public key when decompress is false', async () => {
|
||||
const expectedCompressedKey = signingKeys.userA.publicKey
|
||||
const publicKey = await keystore.getPublic(key, { decompress: false })
|
||||
strictEqual(publicKey, expectedCompressedKey)
|
||||
})
|
||||
|
||||
it('gets the public key buffer when decompressed is false', async () => {
|
||||
const expected = {
|
||||
type: 'Buffer',
|
||||
data: [3, 224, 72, 5, 56, 194, 163, 153,
|
||||
81, 208, 84, 225, 127, 243, 31, 222,
|
||||
72, 124, 177, 3, 29, 0, 68, 160,
|
||||
55, 181, 58, 210, 224, 40, 163, 231,
|
||||
124]
|
||||
}
|
||||
|
||||
const publicKey = await keystore.getPublic(key, { format: 'buffer', decompress: false })
|
||||
|
||||
deepStrictEqual(publicKey.toJSON(), expected)
|
||||
})
|
||||
|
||||
it('throws an error if no keys are passed', async () => {
|
||||
try {
|
||||
await keystore.getPublic()
|
||||
} catch (e) {
|
||||
strictEqual(true, true)
|
||||
}
|
||||
})
|
||||
|
||||
it('throws an error if a bad format is passed', async () => {
|
||||
try {
|
||||
await keystore.getPublic(key, { format: 'foo' })
|
||||
} catch (e) {
|
||||
strictEqual(true, true)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('gets the public key buffer when decompressed is false', async () => {
|
||||
const expected = {
|
||||
type: 'Buffer',
|
||||
data: [3, 224, 72, 5, 56, 194, 163, 153,
|
||||
81, 208, 84, 225, 127, 243, 31, 222,
|
||||
72, 124, 177, 3, 29, 0, 68, 160,
|
||||
55, 181, 58, 210, 224, 40, 163, 231,
|
||||
124]
|
||||
}
|
||||
describe('Verifying', async function () {
|
||||
let key, publicKey
|
||||
|
||||
const publicKey = await keystore.getPublic(key, { format: 'buffer', decompress: false })
|
||||
beforeEach(async () => {
|
||||
key = await keystore.getKey('userA')
|
||||
publicKey = await keystore.getPublic(key)
|
||||
})
|
||||
|
||||
deepStrictEqual(publicKey.toJSON(), expected)
|
||||
})
|
||||
it('verifies content', async () => {
|
||||
const signature = '304402207eb6e4f4b2c56665c505696c41ec0831c6c2998620589d4b6f405d49134dea5102207e71ba37d94b7a70e3d9fb3bea7c8d8b7082c3c880b6831e9613a0a3e7aabd9f'
|
||||
const verified = await verifyMessage(signature, publicKey, 'data data data')
|
||||
strictEqual(verified, true)
|
||||
})
|
||||
|
||||
it('throws an error if no keys are passed', async () => {
|
||||
try {
|
||||
await keystore.getPublic()
|
||||
} catch (e) {
|
||||
strictEqual(true, true)
|
||||
}
|
||||
})
|
||||
it('verifies content with cache', async () => {
|
||||
const data = 'data'.repeat(1024 * 1024)
|
||||
const signature = await signMessage(key, data)
|
||||
const startTime = new Date().getTime()
|
||||
await verifyMessage(signature, publicKey, data)
|
||||
const first = new Date().getTime()
|
||||
await verifyMessage(signature, publicKey, data)
|
||||
const after = new Date().getTime()
|
||||
console.log('First pass:', first - startTime, 'ms', 'Cached:', after - first, 'ms')
|
||||
strictEqual(first - startTime > after - first, true)
|
||||
})
|
||||
|
||||
it('throws an error if a bad format is passed', async () => {
|
||||
try {
|
||||
await keystore.getPublic(key, { format: 'foo' })
|
||||
} catch (e) {
|
||||
strictEqual(true, true)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('Verifying', async function () {
|
||||
let key, publicKey
|
||||
|
||||
beforeEach(async () => {
|
||||
key = await keystore.getKey('userA')
|
||||
publicKey = await keystore.getPublic(key)
|
||||
})
|
||||
|
||||
it('verifies content', async () => {
|
||||
const signature = '304402207eb6e4f4b2c56665c505696c41ec0831c6c2998620589d4b6f405d49134dea5102207e71ba37d94b7a70e3d9fb3bea7c8d8b7082c3c880b6831e9613a0a3e7aabd9f'
|
||||
const verified = await verifyMessage(signature, publicKey, 'data data data')
|
||||
strictEqual(verified, true)
|
||||
})
|
||||
|
||||
it('verifies content with cache', async () => {
|
||||
const data = 'data'.repeat(1024 * 1024)
|
||||
const signature = await signMessage(key, data)
|
||||
const startTime = new Date().getTime()
|
||||
await verifyMessage(signature, publicKey, data)
|
||||
const first = new Date().getTime()
|
||||
await verifyMessage(signature, publicKey, data)
|
||||
const after = new Date().getTime()
|
||||
console.log('First pass:', first - startTime, 'ms', 'Cached:', after - first, 'ms')
|
||||
strictEqual(first - startTime > after - first, true)
|
||||
})
|
||||
|
||||
it('does not verify content with bad signature', async () => {
|
||||
const signature = 'xxxxxx'
|
||||
const verified = await verifyMessage(signature, publicKey, 'data data data')
|
||||
strictEqual(verified, false)
|
||||
it('does not verify content with bad signature', async () => {
|
||||
const signature = 'xxxxxx'
|
||||
const verified = await verifyMessage(signature, publicKey, 'data data data')
|
||||
strictEqual(verified, false)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
Loading…
x
Reference in New Issue
Block a user