diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c2984425..a6d97d241 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # 2.9.0-beta.x - Added `createKeyMulti` & `createKeySub` to create utility sub & multi keys +- Added `u8aSorted` to sort `Uint8Arrays` +- Added `sortAddresses` to sort addresses based on their internal representation # 2.8.1 Apr 9, 2020 diff --git a/packages/util-crypto/src/address/index.ts b/packages/util-crypto/src/address/index.ts index eeaf7531e..b802dfb2f 100644 --- a/packages/util-crypto/src/address/index.ts +++ b/packages/util-crypto/src/address/index.ts @@ -9,6 +9,7 @@ import createKeySub from './keySub'; import decodeAddress from './decode'; import encodeAddress from './encode'; import setSS58Format from './setSS58Format'; +import sortAddresses from './sort'; export { checkAddress, @@ -17,5 +18,6 @@ export { createKeySub, decodeAddress, encodeAddress, - setSS58Format + setSS58Format, + sortAddresses }; diff --git a/packages/util-crypto/src/address/keyMulti.spec.ts b/packages/util-crypto/src/address/keyMulti.spec.ts index 037c04f35..cfc81ba94 100644 --- a/packages/util-crypto/src/address/keyMulti.spec.ts +++ b/packages/util-crypto/src/address/keyMulti.spec.ts @@ -2,21 +2,7 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import createKeyMulti, { sortU8a } from './keyMulti'; - -describe('sortU8a', (): void => { - it('sorts a simple set of u8a', (): void => { - expect( - sortU8a([new Uint8Array([1, 2, 3]), new Uint8Array([4, 5, 6]), new Uint8Array([2, 3, 4])]) - ).toEqual([new Uint8Array([1, 2, 3]), new Uint8Array([2, 3, 4]), new Uint8Array([4, 5, 6])]); - }); - - it('sorts a simple set of u8a (not the same lengths)', (): void => { - expect( - sortU8a([new Uint8Array([1, 2, 3, 4]), new Uint8Array([4, 5, 6]), new Uint8Array([1, 2, 3, 5])]) - ).toEqual([new Uint8Array([1, 2, 3, 4]), new Uint8Array([1, 2, 3, 5]), new Uint8Array([4, 5, 6])]); - }); -}); +import { createKeyMulti } from '.'; describe('createKeyMulti', (): void => { it('creates a valid multikey (aligning with Rust, needs sorting)', (): void => { diff --git a/packages/util-crypto/src/address/keyMulti.ts b/packages/util-crypto/src/address/keyMulti.ts index 3cedbaa6d..0ad2e943c 100644 --- a/packages/util-crypto/src/address/keyMulti.ts +++ b/packages/util-crypto/src/address/keyMulti.ts @@ -3,42 +3,18 @@ // of the Apache-2.0 license. See the LICENSE file for details. import BN from 'bn.js'; -import { bnToU8a, compactToU8a, isUndefined, u8aConcat } from '@polkadot/util'; +import { bnToU8a, compactToU8a, u8aConcat, u8aSorted } from '@polkadot/util'; import blake2AsU8a from '../blake2/asU8a'; import decodeAddress from './decode'; -export function sortU8a (u8as: Uint8Array[]): Uint8Array[] { - return u8as.sort((a, b): number => { - let i = 0; - - while (true) { - if (isUndefined(a[i]) && isUndefined(b[i])) { - return 0; - } else if (isUndefined(a[i])) { - return -1; - } else if (isUndefined(b[i])) { - return 1; - } - - const cmp = a[i] - b[i]; - - if (cmp !== 0) { - return cmp; - } - - i++; - } - }); -} - -export default function createKeyMulti (who: (Uint8Array | string)[], index: BigInt | BN | number): Uint8Array { +export default function createKeyMulti (who: (Uint8Array | string)[], threshold: BigInt | BN | number): Uint8Array { return blake2AsU8a( u8aConcat( 'modlpy/utilisuba', compactToU8a(who.length), - ...sortU8a(who.map((who) => decodeAddress(who))), - bnToU8a(index, { bitLength: 16, isLe: true }) + ...u8aSorted(who.map((who) => decodeAddress(who))), + bnToU8a(threshold, { bitLength: 16, isLe: true }) ) ); } diff --git a/packages/util-crypto/src/address/sort.spec.ts b/packages/util-crypto/src/address/sort.spec.ts new file mode 100644 index 000000000..972e5feca --- /dev/null +++ b/packages/util-crypto/src/address/sort.spec.ts @@ -0,0 +1,21 @@ +// Copyright 2017-2020 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { sortAddresses } from '.'; + +describe('sortAddresses', (): void => { + it('sorts addresses by the publicKeys', (): void => { + expect( + sortAddresses([ + '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY', + '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty', + '5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y' + ]) + ).toEqual([ + '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty', + '5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y', + '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY' + ]); + }); +}); diff --git a/packages/util-crypto/src/address/sort.ts b/packages/util-crypto/src/address/sort.ts new file mode 100644 index 000000000..e5a89f044 --- /dev/null +++ b/packages/util-crypto/src/address/sort.ts @@ -0,0 +1,14 @@ +// Copyright 2017-2020 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { u8aSorted } from '@polkadot/util'; + +import decodeAddress from './decode'; +import encodeAddress from './encode'; + +export default function sortAddresses (addresses: (Uint8Array | string)[]): string[] { + return u8aSorted( + addresses.map((who) => decodeAddress(who)) + ).map((u8a) => encodeAddress(u8a)); +} diff --git a/packages/util/src/u8a/index.ts b/packages/util/src/u8a/index.ts index 8e8b6c661..2069b178d 100644 --- a/packages/util/src/u8a/index.ts +++ b/packages/util/src/u8a/index.ts @@ -8,6 +8,7 @@ export { default as u8aConcat } from './concat'; export { default as u8aFixLength } from './fixLength'; +export { default as u8aSorted } from './sorted'; export { default as u8aToBn } from './toBn'; export { default as u8aToBuffer } from './toBuffer'; export { default as u8aToHex } from './toHex'; diff --git a/packages/util/src/u8a/sorted.spec.ts b/packages/util/src/u8a/sorted.spec.ts new file mode 100644 index 000000000..31469b4cb --- /dev/null +++ b/packages/util/src/u8a/sorted.spec.ts @@ -0,0 +1,19 @@ +// Copyright 2017-2020 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { u8aSorted } from '.'; + +describe('u8aSorted', (): void => { + it('sorts a simple set of u8a', (): void => { + expect( + u8aSorted([new Uint8Array([1, 2, 3]), new Uint8Array([4, 5, 6]), new Uint8Array([2, 3, 4])]) + ).toEqual([new Uint8Array([1, 2, 3]), new Uint8Array([2, 3, 4]), new Uint8Array([4, 5, 6])]); + }); + + it('sorts a simple set of u8a (not the same lengths)', (): void => { + expect( + u8aSorted([new Uint8Array([1, 2, 3, 4]), new Uint8Array([4, 5, 6]), new Uint8Array([1, 2, 3, 5])]) + ).toEqual([new Uint8Array([1, 2, 3, 4]), new Uint8Array([1, 2, 3, 5]), new Uint8Array([4, 5, 6])]); + }); +}); diff --git a/packages/util/src/u8a/sorted.ts b/packages/util/src/u8a/sorted.ts new file mode 100644 index 000000000..368d75873 --- /dev/null +++ b/packages/util/src/u8a/sorted.ts @@ -0,0 +1,29 @@ +// Copyright 2017-2020 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import isUndefined from '../is/undefined'; + +export default function u8aSorted (u8as: Uint8Array[]): Uint8Array[] { + return u8as.sort((a, b): number => { + let i = 0; + + while (true) { + if (isUndefined(a[i]) && isUndefined(b[i])) { + return 0; + } else if (isUndefined(a[i])) { + return -1; + } else if (isUndefined(b[i])) { + return 1; + } + + const cmp = a[i] - b[i]; + + if (cmp !== 0) { + return cmp; + } + + i++; + } + }); +}