Use @noble/secp256k1 (#1189)
* Use @noble/secp256k1 * Remove elliptic * Remove @types * -19 * verify uses recover internally * fromSeed * Init Noble utils inside cryptoInit * remove bn.js dep (used from @polkadot/util) * With x-noble-secp256k1 * Adjust * Remove unused functions * CHANGELOG * Fix useless assignment * Cleanup cryptoInit * adjust hasher * bn.js only via util * align versions * Remove x-secp256k1 (to be pulled in from master merge) * Remove (now) invalid test * Remove assertBigInt * hasBigInt * cleanups * Apply suggestions from code review * evmAddress fixes * Extra comment
This commit is contained in:
@@ -5,9 +5,9 @@ import type { HexString } from '@polkadot/util/types';
|
||||
import type { HashType } from '../secp256k1/types';
|
||||
import type { Prefix } from './types';
|
||||
|
||||
import { u8aConcat } from '@polkadot/util';
|
||||
import { assert, u8aConcat } from '@polkadot/util';
|
||||
|
||||
import { secp256k1Hasher } from '../secp256k1/hasher';
|
||||
import { hasher } from '../secp256k1/hasher';
|
||||
import { encodeAddress } from './encode';
|
||||
|
||||
/**
|
||||
@@ -15,15 +15,9 @@ import { encodeAddress } from './encode';
|
||||
* @summary Converts an EVM address to its corresponding SS58 address.
|
||||
*/
|
||||
export function evmToAddress (evmAddress: HexString | string | Uint8Array, ss58Format?: Prefix, hashType: HashType = 'blake2'): string {
|
||||
const wrapError = (message: string) => `Converting ${evmAddress as string}: ${message}`;
|
||||
|
||||
const message = u8aConcat('evm:', evmAddress);
|
||||
|
||||
if (message.length !== 24) {
|
||||
throw new Error(wrapError('Invalid evm address length'));
|
||||
}
|
||||
assert(message.length === 24, () => `Converting ${evmAddress as string}: Invalid evm address length`);
|
||||
|
||||
const address = secp256k1Hasher(hashType, message);
|
||||
|
||||
return encodeAddress(address, ss58Format);
|
||||
return encodeAddress(hasher(hashType, message), ss58Format);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,16 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { u8aConcat } from '@polkadot/util';
|
||||
import { utils as utilsNobleSecp256k1 } from '@polkadot/x-noble-secp256k1';
|
||||
|
||||
import { cryptoWaitReady } from './crypto';
|
||||
import { hmacSha256AsU8a } from './hmac';
|
||||
|
||||
// Set overrides on the secp256k1 utils
|
||||
// - hmacShaSync - This needs to be set, unset by default
|
||||
utilsNobleSecp256k1.hmacSha256Sync = (key: Uint8Array, ...messages: Uint8Array[]) =>
|
||||
hmacSha256AsU8a(key, u8aConcat(...messages));
|
||||
|
||||
// start init process immediately
|
||||
cryptoWaitReady().catch((): void => {
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { assert } from '@polkadot/util';
|
||||
import { assert, hasBigInt } from '@polkadot/util';
|
||||
import { isReady, secp256k1Compress as wasm } from '@polkadot/wasm-crypto';
|
||||
|
||||
import { secp256k1 } from './secp256k1';
|
||||
import { Point } from '@polkadot/x-noble-secp256k1';
|
||||
|
||||
export function secp256k1Compress (publicKey: Uint8Array, onlyJs?: boolean): Uint8Array {
|
||||
if (publicKey.length === 33) {
|
||||
@@ -13,12 +12,7 @@ export function secp256k1Compress (publicKey: Uint8Array, onlyJs?: boolean): Uin
|
||||
|
||||
assert(publicKey.length === 65, 'Invalid publicKey provided');
|
||||
|
||||
return !onlyJs && isReady()
|
||||
return !hasBigInt || (!onlyJs && isReady())
|
||||
? wasm(publicKey)
|
||||
: new Uint8Array(
|
||||
secp256k1
|
||||
.keyFromPublic(publicKey)
|
||||
.getPublic()
|
||||
.encodeCompressed()
|
||||
);
|
||||
: Point.fromHex(publicKey).toRawBytes(true);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { assert, bnToU8a, u8aConcat } from '@polkadot/util';
|
||||
import { assert, bnToU8a, hasBigInt, u8aConcat } from '@polkadot/util';
|
||||
import { isReady, secp256k1Expand as wasm } from '@polkadot/wasm-crypto';
|
||||
import { Point } from '@polkadot/x-noble-secp256k1';
|
||||
|
||||
import { BN_BE_256_OPTS } from '../bn';
|
||||
import { secp256k1 } from './secp256k1';
|
||||
|
||||
export function secp256k1Expand (publicKey: Uint8Array, onlyJs?: boolean): Uint8Array {
|
||||
if (publicKey.length === 65) {
|
||||
@@ -14,16 +14,14 @@ export function secp256k1Expand (publicKey: Uint8Array, onlyJs?: boolean): Uint8
|
||||
|
||||
assert(publicKey.length === 33, 'Invalid publicKey provided');
|
||||
|
||||
if (!onlyJs && isReady()) {
|
||||
if (!hasBigInt || (!onlyJs && isReady())) {
|
||||
return wasm(publicKey).subarray(1);
|
||||
}
|
||||
|
||||
const expanded = secp256k1
|
||||
.keyFromPublic(publicKey)
|
||||
.getPublic();
|
||||
const { x, y } = Point.fromHex(publicKey);
|
||||
|
||||
return u8aConcat(
|
||||
bnToU8a(expanded.getX(), BN_BE_256_OPTS),
|
||||
bnToU8a(expanded.getY(), BN_BE_256_OPTS)
|
||||
bnToU8a(x, BN_BE_256_OPTS),
|
||||
bnToU8a(y, BN_BE_256_OPTS)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { secp256k1Hasher } from './hasher';
|
||||
import { hasher } from './hasher';
|
||||
|
||||
describe('secp256k1Hasher', (): void => {
|
||||
describe('hasher', (): void => {
|
||||
it('creates a blake2 hash', (): void => {
|
||||
expect(
|
||||
secp256k1Hasher('blake2', 'abc')
|
||||
hasher('blake2', 'abc')
|
||||
).toEqual(
|
||||
new Uint8Array([189, 221, 129, 60, 99, 66, 57, 114, 49, 113, 239, 63, 238, 152, 87, 155, 148, 150, 78, 59, 177, 203, 62, 66, 114, 98, 200, 192, 104, 213, 35, 25])
|
||||
);
|
||||
@@ -14,7 +14,7 @@ describe('secp256k1Hasher', (): void => {
|
||||
|
||||
it('creates a keccak hash', (): void => {
|
||||
expect(
|
||||
secp256k1Hasher('keccak', 'abc')
|
||||
hasher('keccak', 'abc')
|
||||
).toEqual(
|
||||
new Uint8Array([78, 3, 101, 122, 234, 69, 169, 79, 199, 212, 123, 168, 38, 200, 214, 103, 192, 209, 230, 227, 58, 100, 160, 54, 236, 68, 245, 143, 161, 45, 108, 69])
|
||||
);
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { HashType } from './types';
|
||||
import { blake2AsU8a } from '../blake2';
|
||||
import { keccakAsU8a } from '../keccak';
|
||||
|
||||
export function secp256k1Hasher (hashType: HashType, data: Uint8Array | string, onlyJs?: boolean): Uint8Array {
|
||||
export function hasher (hashType: HashType, data: Uint8Array | string, onlyJs?: boolean): Uint8Array {
|
||||
return hashType === 'keccak'
|
||||
? keccakAsU8a(data, undefined, onlyJs)
|
||||
: blake2AsU8a(data, undefined, undefined, onlyJs);
|
||||
|
||||
@@ -3,11 +3,9 @@
|
||||
|
||||
import type { Keypair } from '../../types';
|
||||
|
||||
import { assert, bnToU8a } from '@polkadot/util';
|
||||
import { assert, hasBigInt } from '@polkadot/util';
|
||||
import { isReady, secp256k1FromSeed } from '@polkadot/wasm-crypto';
|
||||
|
||||
import { BN_BE_256_OPTS } from '../../bn';
|
||||
import { secp256k1 } from '../secp256k1';
|
||||
import { getPublicKey } from '@polkadot/x-noble-secp256k1';
|
||||
|
||||
/**
|
||||
* @name secp256k1PairFromSeed
|
||||
@@ -16,7 +14,7 @@ import { secp256k1 } from '../secp256k1';
|
||||
export function secp256k1PairFromSeed (seed: Uint8Array, onlyJs?: boolean): Keypair {
|
||||
assert(seed.length === 32, 'Expected valid 32-byte private key as a seed');
|
||||
|
||||
if (!onlyJs && isReady()) {
|
||||
if (!hasBigInt || (!onlyJs && isReady())) {
|
||||
const full = secp256k1FromSeed(seed);
|
||||
|
||||
return {
|
||||
@@ -25,10 +23,8 @@ export function secp256k1PairFromSeed (seed: Uint8Array, onlyJs?: boolean): Keyp
|
||||
};
|
||||
}
|
||||
|
||||
const key = secp256k1.keyFromPrivate(seed);
|
||||
|
||||
return {
|
||||
publicKey: new Uint8Array(key.getPublic().encodeCompressed()),
|
||||
secretKey: bnToU8a(key.getPrivate(), BN_BE_256_OPTS)
|
||||
publicKey: getPublicKey(seed, true),
|
||||
secretKey: seed
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,28 +1,26 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { HexString } from '@polkadot/util/types';
|
||||
import type { HashType } from './types';
|
||||
|
||||
import { assert } from '@polkadot/util';
|
||||
import { assert, hasBigInt, u8aToU8a } from '@polkadot/util';
|
||||
import { isReady, secp256k1Recover as wasm } from '@polkadot/wasm-crypto';
|
||||
import { recoverPublicKey, Signature } from '@polkadot/x-noble-secp256k1';
|
||||
|
||||
import { secp256k1Compress } from './compress';
|
||||
import { secp256k1Expand } from './expand';
|
||||
import { secp256k1 } from './secp256k1';
|
||||
|
||||
/**
|
||||
* @name secp256k1Recover
|
||||
* @description Recovers a publicKey from the supplied signature
|
||||
*/
|
||||
export function secp256k1Recover (message: Uint8Array, signature: Uint8Array, recovery: number, hashType: HashType = 'blake2', onlyJs?: boolean): Uint8Array {
|
||||
const publicKey = !onlyJs && isReady()
|
||||
? wasm(message, signature.slice(0, 64), recovery)
|
||||
: new Uint8Array(
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-argument
|
||||
secp256k1
|
||||
.recoverPubKey(message, { r: signature.slice(0, 32), s: signature.slice(32, 64) }, recovery)
|
||||
.encode(null, true)
|
||||
);
|
||||
export function secp256k1Recover (msgHash: HexString | Uint8Array | string, signature: HexString | Uint8Array | string, recovery: number, hashType: HashType = 'blake2', onlyJs?: boolean): Uint8Array {
|
||||
const sig = u8aToU8a(signature).subarray(0, 64);
|
||||
const msg = u8aToU8a(msgHash);
|
||||
const publicKey = !hasBigInt || (!onlyJs && isReady())
|
||||
? wasm(msg, sig, recovery)
|
||||
: recoverPublicKey(msg, Signature.fromCompact(sig).toRawBytes(), recovery);
|
||||
|
||||
assert(publicKey, 'Unable to recover publicKey from signature');
|
||||
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import elliptic from 'elliptic';
|
||||
|
||||
// eslint-disable-next-line new-cap
|
||||
export const secp256k1 = new elliptic.ec('secp256k1');
|
||||
@@ -5,7 +5,7 @@ import { hexToU8a } from '@polkadot/util';
|
||||
import { waitReady } from '@polkadot/wasm-crypto';
|
||||
|
||||
import { performanceWasm } from '../../test/performance';
|
||||
import { secp256k1PairFromSeed, secp256k1Recover, secp256k1Sign } from '.';
|
||||
import { secp256k1PairFromSeed, secp256k1Sign } from '.';
|
||||
|
||||
const pair = secp256k1PairFromSeed(hexToU8a('0x4380de832af797688026ce24f85204d508243f201650c1a134929e5458b7fbae'));
|
||||
const msg = hexToU8a('0xa30b64ce1eedf409c8afb801d72c05234e64849ea538c15dd3c8cf4ffcf166c9');
|
||||
@@ -20,19 +20,16 @@ describe('sign', (): void => {
|
||||
expect(
|
||||
secp256k1Sign(msg, pair, undefined, onlyJs)
|
||||
).toEqual(hexToU8a(
|
||||
// as of libsecp256k1 0.7.0 the wasm yields different s & v values
|
||||
onlyJs
|
||||
? '0xdf92f73d9f060cefacf187b5414491cb992998ace017fa48839b5cda3e264ba8c4efa521361678d9b8582744d77aa4b8d886d7380b7808a683174afad9c4700300'
|
||||
: '0xdf92f73d9f060cefacf187b5414491cb992998ace017fa48839b5cda3e264ba83b105adec9e9872647a7d8bb28855b45e22805aea3d097953cbb1391f671d13e01'
|
||||
// from elliptic, this is - 0xdf92f73d9f060cefacf187b5414491cb992998ace017fa48839b5cda3e264ba8c4efa521361678d9b8582744d77aa4b8d886d7380b7808a683174afad9c4700300
|
||||
// libsecp256k1 & @noble/hashes do agree here...
|
||||
'0xdf92f73d9f060cefacf187b5414491cb992998ace017fa48839b5cda3e264ba83b105adec9e9872647a7d8bb28855b45e22805aea3d097953cbb1391f671d13e01'
|
||||
));
|
||||
});
|
||||
});
|
||||
|
||||
// since the libsecp256k1 signatures don't match (but can be verified), we
|
||||
// do both signing and verification here (checking the extracted key)
|
||||
performanceWasm('secp256k1Sign/Verify', 100, (_, onlyJs) => {
|
||||
const sig = secp256k1Sign(msg, pair, undefined, onlyJs);
|
||||
|
||||
return secp256k1Recover(msg, sig, sig[64], undefined, onlyJs);
|
||||
});
|
||||
performanceWasm('secp256k1Sign', 1000, (_, onlyJs) =>
|
||||
secp256k1Sign(msg, pair, undefined, onlyJs)
|
||||
);
|
||||
});
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
import type { Keypair } from '../types';
|
||||
import type { HashType } from './types';
|
||||
|
||||
import { assert, bnToU8a, u8aConcat } from '@polkadot/util';
|
||||
import { assert, bnToU8a, hasBigInt, u8aConcat } from '@polkadot/util';
|
||||
import { isReady, secp256k1Sign as wasm } from '@polkadot/wasm-crypto';
|
||||
import { Signature, signSync } from '@polkadot/x-noble-secp256k1';
|
||||
|
||||
import { BN_BE_256_OPTS } from '../bn';
|
||||
import { secp256k1Hasher } from './hasher';
|
||||
import { secp256k1 } from './secp256k1';
|
||||
import { hasher } from './hasher';
|
||||
|
||||
/**
|
||||
* @name secp256k1Sign
|
||||
@@ -18,18 +18,18 @@ import { secp256k1 } from './secp256k1';
|
||||
export function secp256k1Sign (message: Uint8Array | string, { secretKey }: Partial<Keypair>, hashType: HashType = 'blake2', onlyJs?: boolean): Uint8Array {
|
||||
assert(secretKey?.length === 32, 'Expected valid secp256k1 secretKey, 32-bytes');
|
||||
|
||||
const data = secp256k1Hasher(hashType, message, onlyJs);
|
||||
const data = hasher(hashType, message, onlyJs);
|
||||
|
||||
if (!onlyJs && isReady()) {
|
||||
if (!hasBigInt || (!onlyJs && isReady())) {
|
||||
return wasm(data, secretKey);
|
||||
}
|
||||
|
||||
const key = secp256k1.keyFromPrivate(secretKey);
|
||||
const ecsig = key.sign(data);
|
||||
const [sigBytes, recoveryParam] = signSync(data, secretKey, { canonical: true, recovered: true });
|
||||
const { r, s } = Signature.fromHex(sigBytes);
|
||||
|
||||
return u8aConcat(
|
||||
bnToU8a(ecsig.r, BN_BE_256_OPTS),
|
||||
bnToU8a(ecsig.s, BN_BE_256_OPTS),
|
||||
new Uint8Array([ecsig.recoveryParam || 0])
|
||||
bnToU8a(r, BN_BE_256_OPTS),
|
||||
bnToU8a(s, BN_BE_256_OPTS),
|
||||
new Uint8Array([recoveryParam || 0])
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import '../cryptoInit';
|
||||
|
||||
import { stringToU8a } from '@polkadot/util';
|
||||
|
||||
import { randomAsU8a } from '../random/asU8a';
|
||||
import { secp256k1Hasher } from './hasher';
|
||||
import { hasher } from './hasher';
|
||||
import { secp256k1Expand, secp256k1PairFromSeed, secp256k1Sign, secp256k1Verify } from '.';
|
||||
|
||||
const MESSAGE = stringToU8a('this is a message');
|
||||
@@ -27,7 +29,7 @@ describe('sign and verify', (): void => {
|
||||
it('signs/verifies a message by random key (blake2)', (): void => {
|
||||
const pair = secp256k1PairFromSeed(randomAsU8a());
|
||||
const signature = secp256k1Sign(MESSAGE, pair);
|
||||
const address = secp256k1Hasher('blake2', pair.publicKey);
|
||||
const address = hasher('blake2', pair.publicKey);
|
||||
|
||||
expect(secp256k1Verify(MESSAGE, signature, address)).toEqual(true);
|
||||
});
|
||||
@@ -35,7 +37,7 @@ describe('sign and verify', (): void => {
|
||||
it('signs/verifies a message by random key (keccak)', (): void => {
|
||||
const pair = secp256k1PairFromSeed(randomAsU8a());
|
||||
const signature = secp256k1Sign(MESSAGE, pair, 'keccak');
|
||||
const address = secp256k1Hasher('keccak', secp256k1Expand(pair.publicKey));
|
||||
const address = hasher('keccak', secp256k1Expand(pair.publicKey));
|
||||
|
||||
expect(secp256k1Verify(MESSAGE, signature, address, 'keccak')).toEqual(true);
|
||||
});
|
||||
@@ -43,7 +45,7 @@ describe('sign and verify', (): void => {
|
||||
it('fails verification on hasher mismatches', (): void => {
|
||||
const pair = secp256k1PairFromSeed(randomAsU8a());
|
||||
const signature = secp256k1Sign(MESSAGE, pair, 'keccak');
|
||||
const address = secp256k1Hasher('keccak', secp256k1Expand(pair.publicKey));
|
||||
const address = hasher('keccak', secp256k1Expand(pair.publicKey));
|
||||
|
||||
expect(secp256k1Verify(MESSAGE, signature, address, 'blake2')).toEqual(false);
|
||||
});
|
||||
@@ -57,7 +59,7 @@ describe('sign and verify', (): void => {
|
||||
secp256k1Verify(
|
||||
MESSAGE,
|
||||
secp256k1Sign(MESSAGE, pair, 'blake2'),
|
||||
secp256k1Hasher('blake2', pair.publicKey),
|
||||
hasher('blake2', pair.publicKey),
|
||||
'blake2'
|
||||
)
|
||||
).toEqual(true);
|
||||
@@ -77,7 +79,7 @@ describe('sign and verify', (): void => {
|
||||
secp256k1Verify(
|
||||
MESSAGE,
|
||||
secp256k1Sign(MESSAGE, pair, 'keccak'),
|
||||
secp256k1Hasher('keccak', secp256k1Expand(pair.publicKey)),
|
||||
hasher('keccak', secp256k1Expand(pair.publicKey)),
|
||||
'keccak'
|
||||
)
|
||||
).toEqual(true);
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import '../cryptoInit';
|
||||
|
||||
import { hexToU8a } from '@polkadot/util';
|
||||
import { waitReady } from '@polkadot/wasm-crypto';
|
||||
|
||||
import { performanceWasm } from '../../test/performance';
|
||||
import { secp256k1Hasher } from './hasher';
|
||||
import { hasher } from './hasher';
|
||||
import { secp256k1PairFromSeed, secp256k1Verify } from '.';
|
||||
|
||||
const message = 'Pay KSMs to the Kusama account:88dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0ee';
|
||||
@@ -33,7 +35,7 @@ describe('secp256k1Verify', (): void => {
|
||||
const msg = hexToU8a('0xa30b64ce1eedf409c8afb801d72c05234e64849ea538c15dd3c8cf4ffcf166c9');
|
||||
const addr = isPublic
|
||||
? pair.publicKey
|
||||
: secp256k1Hasher('blake2', pair.publicKey, onlyJs);
|
||||
: hasher('blake2', pair.publicKey, onlyJs);
|
||||
|
||||
it('signature from JS', (): void => {
|
||||
expect(
|
||||
|
||||
@@ -6,26 +6,20 @@ import type { HashType } from './types';
|
||||
|
||||
import { assert, u8aEq, u8aToU8a } from '@polkadot/util';
|
||||
|
||||
import { secp256k1Hasher } from './hasher';
|
||||
import { hasher } from './hasher';
|
||||
import { secp256k1Recover } from './recover';
|
||||
|
||||
/**
|
||||
* @name secp256k1Verify
|
||||
* @description Verifies the signature of `message`, using the supplied pair
|
||||
*/
|
||||
export function secp256k1Verify (message: HexString | Uint8Array | string, signature: HexString | Uint8Array | string, address: HexString | Uint8Array | string, hashType: HashType = 'blake2', onlyJs?: boolean): boolean {
|
||||
const u8a = u8aToU8a(signature);
|
||||
export function secp256k1Verify (msgHash: HexString | Uint8Array | string, signature: HexString | Uint8Array | string, address: HexString | Uint8Array | string, hashType: HashType = 'blake2', onlyJs?: boolean): boolean {
|
||||
const sig = u8aToU8a(signature);
|
||||
|
||||
assert(u8a.length === 65, `Expected signature with 65 bytes, ${u8a.length} found instead`);
|
||||
assert(sig.length === 65, `Expected signature with 65 bytes, ${sig.length} found instead`);
|
||||
|
||||
const publicKey = secp256k1Recover(
|
||||
secp256k1Hasher(hashType, message),
|
||||
u8a.subarray(0, 64),
|
||||
u8a[64],
|
||||
hashType,
|
||||
onlyJs
|
||||
);
|
||||
const signerAddr = secp256k1Hasher(hashType, publicKey, onlyJs);
|
||||
const publicKey = secp256k1Recover(hasher(hashType, msgHash), sig, sig[64], hashType, onlyJs);
|
||||
const signerAddr = hasher(hashType, publicKey, onlyJs);
|
||||
const inputAddr = u8aToU8a(address);
|
||||
|
||||
// for Ethereum (keccak) the last 20 bytes is the address
|
||||
|
||||
Reference in New Issue
Block a user