From cb426f36ada2eecc6b68799ab50b9928f4ea08a5 Mon Sep 17 00:00:00 2001 From: Mariusz Przybylski Date: Mon, 7 Oct 2019 01:29:33 +0100 Subject: [PATCH 1/3] Implement KeyGenerator --- src/core/format/KeyGenerator.ts | 38 +++++++++++++++++++++++ src/core/format/index.ts | 1 + test/core/format/KeyGenerator.spec.ts | 44 +++++++++++++++++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 src/core/format/KeyGenerator.ts create mode 100644 test/core/format/KeyGenerator.spec.ts diff --git a/src/core/format/KeyGenerator.ts b/src/core/format/KeyGenerator.ts new file mode 100644 index 0000000000..cc562d421e --- /dev/null +++ b/src/core/format/KeyGenerator.ts @@ -0,0 +1,38 @@ +/* + * Copyright 2018 NEM + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { UInt64 } from '../../model/UInt64'; +import { sha3_256 } from 'js-sha3'; + +export class KeyGenerator { + /** + * @returns {UInt64} Deterministic uint64 value for the given string + */ + public static fromString(input: string) { + if (input.length === 0) { + throw Error(`Input must not be empty`); + } + if (input.length > 1024) { + throw Error(`Input exceeds 1024 characters (has ${input.length})`); + } + const format = /^[a-zA-Z0-9_]+$/gm; + if (input.match(format) === null) { + throw Error(`Input has invalid format (accepted characters: a-z, A-Z, 0-9, _)`); + } + const hex = sha3_256(input) + return UInt64.fromHex(hex.substr(0, 16)) + } +} diff --git a/src/core/format/index.ts b/src/core/format/index.ts index 27fc8ea316..45d6ee1ca4 100644 --- a/src/core/format/index.ts +++ b/src/core/format/index.ts @@ -18,4 +18,5 @@ export * from './RawAddress'; export * from './RawArray'; export * from './Convert'; export * from './IdGenerator'; +export * from './KeyGenerator'; export * from './RawUInt64'; diff --git a/test/core/format/KeyGenerator.spec.ts b/test/core/format/KeyGenerator.spec.ts new file mode 100644 index 0000000000..d376e5930c --- /dev/null +++ b/test/core/format/KeyGenerator.spec.ts @@ -0,0 +1,44 @@ +/* + * Copyright 2019 NEM + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { expect } from 'chai'; +import { UInt64 } from '../../../src/model/UInt64'; +import { KeyGenerator } from '../../../src/core/format/KeyGenerator'; + +describe('key generator', () => { + describe('generate key from string', () => { + it('throws if input is empty', () => { + expect(() => KeyGenerator.fromString('')).to.throw(Error, 'Input must not be empty'); + }) + it('returns UInt64', () => { + expect(KeyGenerator.fromString('a')).to.be.instanceOf(UInt64); + }) + it('throws if input has invalid format', () => { + expect(() => KeyGenerator.fromString('$abc')).to.throw(Error, ''); + expect(() => KeyGenerator.fromString('ab-c')).to.throw(Error, ''); + expect(() => KeyGenerator.fromString('abc.')).to.throw(Error, ''); + }) + it('generates correct keys', () => { + expect(KeyGenerator.fromString('a').toHex()).to.equal('80084BF2FBA02475'); + }) + it('generates keys deterministically', () => { + expect(KeyGenerator.fromString('abc').toHex()).to.equal('3A985DA74FE225B2'); + expect(KeyGenerator.fromString('abc').toHex()).to.equal('3A985DA74FE225B2'); + expect(KeyGenerator.fromString('def').toHex()).to.equal('8E0D8F672252ACB0'); + expect(KeyGenerator.fromString('def').toHex()).to.equal('8E0D8F672252ACB0'); + expect(KeyGenerator.fromString('abc').toHex()).to.equal('3A985DA74FE225B2'); + }) + }) +}); From e39cf99bd778fbee8c8effac5486abfe2dd76855 Mon Sep 17 00:00:00 2001 From: Mariusz Przybylski Date: Mon, 7 Oct 2019 10:35:21 +0100 Subject: [PATCH 2/3] Fix copyright, jsdoc and return value type. Remove regex --- src/core/format/KeyGenerator.ts | 10 ++++------ test/core/format/KeyGenerator.spec.ts | 5 ----- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/core/format/KeyGenerator.ts b/src/core/format/KeyGenerator.ts index cc562d421e..602df03b32 100644 --- a/src/core/format/KeyGenerator.ts +++ b/src/core/format/KeyGenerator.ts @@ -1,5 +1,5 @@ /* - * Copyright 2018 NEM + * Copyright 2019 NEM * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,19 +19,17 @@ import { sha3_256 } from 'js-sha3'; export class KeyGenerator { /** + * Generate UInt64 from a string + * @param {string} input Input string * @returns {UInt64} Deterministic uint64 value for the given string */ - public static fromString(input: string) { + public static fromString(input: string): UInt64 { if (input.length === 0) { throw Error(`Input must not be empty`); } if (input.length > 1024) { throw Error(`Input exceeds 1024 characters (has ${input.length})`); } - const format = /^[a-zA-Z0-9_]+$/gm; - if (input.match(format) === null) { - throw Error(`Input has invalid format (accepted characters: a-z, A-Z, 0-9, _)`); - } const hex = sha3_256(input) return UInt64.fromHex(hex.substr(0, 16)) } diff --git a/test/core/format/KeyGenerator.spec.ts b/test/core/format/KeyGenerator.spec.ts index d376e5930c..bcde2279d9 100644 --- a/test/core/format/KeyGenerator.spec.ts +++ b/test/core/format/KeyGenerator.spec.ts @@ -25,11 +25,6 @@ describe('key generator', () => { it('returns UInt64', () => { expect(KeyGenerator.fromString('a')).to.be.instanceOf(UInt64); }) - it('throws if input has invalid format', () => { - expect(() => KeyGenerator.fromString('$abc')).to.throw(Error, ''); - expect(() => KeyGenerator.fromString('ab-c')).to.throw(Error, ''); - expect(() => KeyGenerator.fromString('abc.')).to.throw(Error, ''); - }) it('generates correct keys', () => { expect(KeyGenerator.fromString('a').toHex()).to.equal('80084BF2FBA02475'); }) From 11934dcdabca3cc751bdf4e6e6b41316f10a32d1 Mon Sep 17 00:00:00 2001 From: Mariusz Przybylski Date: Mon, 7 Oct 2019 19:46:52 +0100 Subject: [PATCH 3/3] Rename, relax validation, convert to arraybuf instead of hex --- src/core/format/KeyGenerator.ts | 10 ++++------ test/core/format/KeyGenerator.spec.ts | 16 ++++++++-------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/core/format/KeyGenerator.ts b/src/core/format/KeyGenerator.ts index 602df03b32..6bb813ec55 100644 --- a/src/core/format/KeyGenerator.ts +++ b/src/core/format/KeyGenerator.ts @@ -23,14 +23,12 @@ export class KeyGenerator { * @param {string} input Input string * @returns {UInt64} Deterministic uint64 value for the given string */ - public static fromString(input: string): UInt64 { + public static generateUInt64Key(input: string): UInt64 { if (input.length === 0) { throw Error(`Input must not be empty`); } - if (input.length > 1024) { - throw Error(`Input exceeds 1024 characters (has ${input.length})`); - } - const hex = sha3_256(input) - return UInt64.fromHex(hex.substr(0, 16)) + const buf = sha3_256.arrayBuffer(input); + const result = new Uint32Array(buf); + return new UInt64([result[0], (result[1] | 0x80000000) >>> 0]); } } diff --git a/test/core/format/KeyGenerator.spec.ts b/test/core/format/KeyGenerator.spec.ts index bcde2279d9..23c4749eb5 100644 --- a/test/core/format/KeyGenerator.spec.ts +++ b/test/core/format/KeyGenerator.spec.ts @@ -20,20 +20,20 @@ import { KeyGenerator } from '../../../src/core/format/KeyGenerator'; describe('key generator', () => { describe('generate key from string', () => { it('throws if input is empty', () => { - expect(() => KeyGenerator.fromString('')).to.throw(Error, 'Input must not be empty'); + expect(() => KeyGenerator.generateUInt64Key('')).to.throw(Error, 'Input must not be empty'); }) it('returns UInt64', () => { - expect(KeyGenerator.fromString('a')).to.be.instanceOf(UInt64); + expect(KeyGenerator.generateUInt64Key('a')).to.be.instanceOf(UInt64); }) it('generates correct keys', () => { - expect(KeyGenerator.fromString('a').toHex()).to.equal('80084BF2FBA02475'); + expect(KeyGenerator.generateUInt64Key('a').toHex()).to.equal('F524A0FBF24B0880'); }) it('generates keys deterministically', () => { - expect(KeyGenerator.fromString('abc').toHex()).to.equal('3A985DA74FE225B2'); - expect(KeyGenerator.fromString('abc').toHex()).to.equal('3A985DA74FE225B2'); - expect(KeyGenerator.fromString('def').toHex()).to.equal('8E0D8F672252ACB0'); - expect(KeyGenerator.fromString('def').toHex()).to.equal('8E0D8F672252ACB0'); - expect(KeyGenerator.fromString('abc').toHex()).to.equal('3A985DA74FE225B2'); + expect(KeyGenerator.generateUInt64Key('abc').toHex()).to.equal('B225E24FA75D983A'); + expect(KeyGenerator.generateUInt64Key('abc').toHex()).to.equal('B225E24FA75D983A'); + expect(KeyGenerator.generateUInt64Key('def').toHex()).to.equal('B0AC5222678F0D8E'); + expect(KeyGenerator.generateUInt64Key('def').toHex()).to.equal('B0AC5222678F0D8E'); + expect(KeyGenerator.generateUInt64Key('abc').toHex()).to.equal('B225E24FA75D983A'); }) }) });