sr25519: switch from wasm to micro-sr25519 (#1971)

* sr25519: switch from wasm to micro-sr25519

* commit yarn lockfile

* Update to scure/sr25519 v0.2

* Fix build

* Commit
This commit is contained in:
Paul Miller
2025-12-08 14:10:16 +01:00
committed by GitHub
parent bf63a0ebf6
commit 57551b38a0
17 changed files with 70 additions and 52 deletions

View File

@@ -36,6 +36,7 @@
"@polkadot/x-bigint": "13.5.9",
"@polkadot/x-randomvalues": "13.5.9",
"@scure/base": "^1.1.7",
"@scure/sr25519": "^0.2.0",
"tslib": "^2.8.0"
},
"peerDependencies": {

View File

@@ -6,7 +6,6 @@
import type { Keypair } from '../types.js';
import { u8aToHex } from '@polkadot/util';
import { waitReady } from '@polkadot/wasm-crypto';
import { sr25519Agreement, sr25519PairFromSeed } from './index.js';
@@ -14,9 +13,7 @@ describe('agreement', (): void => {
let pairA: Keypair;
let pairB: Keypair;
beforeEach(async (): Promise<void> => {
await waitReady();
beforeEach((): void => {
pairA = sr25519PairFromSeed('0x98b3d305d5a5eace562387e47e59badd4d77e3f72cabfb10a60f8a197059f0a8');
pairB = sr25519PairFromSeed('0x9732eea001851ff862d949a1699c9971f3a26edbede2ad7922cbbe9a0701f366');
});

View File

@@ -1,8 +1,9 @@
// Copyright 2017-2025 @polkadot/util-crypto authors & contributors
// SPDX-License-Identifier: Apache-2.0
import { getSharedSecret } from '@scure/sr25519';
import { u8aToU8a } from '@polkadot/util';
import { sr25519Agree } from '@polkadot/wasm-crypto';
/**
* @name sr25519Agreement
@@ -18,5 +19,5 @@ export function sr25519Agreement (secretKey: string | Uint8Array, publicKey: str
throw new Error(`Invalid secretKey, received ${secretKeyU8a.length} bytes, expected 64`);
}
return sr25519Agree(publicKeyU8a, secretKeyU8a);
return getSharedSecret(secretKeyU8a, publicKeyU8a);
}

View File

@@ -3,10 +3,9 @@
import type { Keypair } from '../types.js';
import { isU8a } from '@polkadot/util';
import * as sr25519 from '@scure/sr25519';
import { sr25519PairFromU8a } from './pair/fromU8a.js';
import { sr25519KeypairToU8a } from './pair/toU8a.js';
import { isU8a } from '@polkadot/util';
export function createDeriveFn (derive: (pair: Uint8Array, cc: Uint8Array) => Uint8Array): (keypair: Keypair, chainCode: Uint8Array) => Keypair {
return (keypair: Keypair, chainCode: Uint8Array): Keypair => {
@@ -14,8 +13,9 @@ export function createDeriveFn (derive: (pair: Uint8Array, cc: Uint8Array) => Ui
throw new Error('Invalid chainCode passed to derive');
}
return sr25519PairFromU8a(
derive(sr25519KeypairToU8a(keypair), chainCode)
);
const secretKey = derive(keypair.secretKey, chainCode);
const publicKey = sr25519.getPublicKey(secretKey);
return { publicKey, secretKey };
};
}

View File

@@ -1,8 +1,9 @@
// Copyright 2017-2025 @polkadot/util-crypto authors & contributors
// SPDX-License-Identifier: Apache-2.0
import { sr25519DeriveKeypairHard } from '@polkadot/wasm-crypto';
import * as sr25519 from '@scure/sr25519';
import { createDeriveFn } from './derive.js';
export const sr25519DeriveHard = /*#__PURE__*/ createDeriveFn(sr25519DeriveKeypairHard);
// eslint-disable-next-line @typescript-eslint/unbound-method
export const sr25519DeriveHard = /*#__PURE__*/ createDeriveFn(sr25519.HDKD.secretHard);

View File

@@ -1,8 +1,9 @@
// Copyright 2017-2025 @polkadot/util-crypto authors & contributors
// SPDX-License-Identifier: Apache-2.0
import * as sr25519 from '@scure/sr25519';
import { isU8a, u8aToU8a } from '@polkadot/util';
import { sr25519DerivePublicSoft } from '@polkadot/wasm-crypto';
export function sr25519DerivePublic (publicKey: string | Uint8Array, chainCode: Uint8Array): Uint8Array {
const publicKeyU8a = u8aToU8a(publicKey);
@@ -13,5 +14,5 @@ export function sr25519DerivePublic (publicKey: string | Uint8Array, chainCode:
throw new Error(`Invalid publicKey, received ${publicKeyU8a.length} bytes, expected 32`);
}
return sr25519DerivePublicSoft(publicKeyU8a, chainCode);
return sr25519.HDKD.publicSoft(publicKeyU8a, chainCode);
}

View File

@@ -1,8 +1,9 @@
// Copyright 2017-2025 @polkadot/util-crypto authors & contributors
// SPDX-License-Identifier: Apache-2.0
import { sr25519DeriveKeypairSoft } from '@polkadot/wasm-crypto';
import * as sr25519 from '@scure/sr25519';
import { createDeriveFn } from './derive.js';
export const sr25519DeriveSoft = /*#__PURE__*/ createDeriveFn(sr25519DeriveKeypairSoft);
// eslint-disable-next-line @typescript-eslint/unbound-method
export const sr25519DeriveSoft = /*#__PURE__*/ createDeriveFn(sr25519.HDKD.secretSoft);

View File

@@ -4,7 +4,6 @@
/// <reference types="@polkadot/dev-test/globals.d.ts" />
import { stringToU8a, u8aToHex } from '@polkadot/util';
import { waitReady } from '@polkadot/wasm-crypto';
import { mnemonicToMiniSecret } from '../../mnemonic/index.js';
import { sr25519PairFromSeed } from '../index.js';
@@ -17,10 +16,6 @@ describe('sr25519PairFromSeed', (): void => {
secretKey: new Uint8Array([240, 16, 102, 96, 195, 221, 162, 63, 22, 218, 169, 172, 91, 129, 27, 150, 48, 119, 245, 188, 10, 248, 159, 133, 128, 79, 13, 232, 228, 36, 240, 80, 249, 141, 102, 243, 148, 66, 80, 111, 249, 71, 253, 145, 31, 24, 199, 167, 165, 218, 99, 154, 99, 232, 211, 180, 226, 51, 247, 65, 67, 217, 81, 193])
};
beforeEach(async (): Promise<void> => {
await waitReady();
});
it('generates a valid publicKey/secretKey pair (u8a)', (): void => {
expect(
sr25519PairFromSeed(TEST)

View File

@@ -3,10 +3,9 @@
import type { Keypair } from '../../types.js';
import { u8aToU8a } from '@polkadot/util';
import { sr25519KeypairFromSeed } from '@polkadot/wasm-crypto';
import * as sr25519 from '@scure/sr25519';
import { sr25519PairFromU8a } from './fromU8a.js';
import { u8aToU8a } from '@polkadot/util';
/**
* @name sr25519PairFromSeed
@@ -19,7 +18,11 @@ export function sr25519PairFromSeed (seed: string | Uint8Array): Keypair {
throw new Error(`Expected a seed matching 32 bytes, found ${seedU8a.length}`);
}
return sr25519PairFromU8a(
sr25519KeypairFromSeed(seedU8a)
);
const sec = sr25519.secretFromSeed(seedU8a);
const pub = sr25519.getPublicKey(sec);
return {
publicKey: pub,
secretKey: sec
};
}

View File

@@ -4,7 +4,6 @@
/// <reference types="@polkadot/dev-test/globals.d.ts" />
import { stringToU8a } from '@polkadot/util';
import { waitReady } from '@polkadot/wasm-crypto';
import { randomAsU8a } from '../random/asU8a.js';
import { sr25519PairFromSeed } from './pair/fromSeed.js';
@@ -13,10 +12,6 @@ import { sr25519Sign } from './sign.js';
const MESSAGE = stringToU8a('this is a message');
describe('sign', (): void => {
beforeEach(async (): Promise<void> => {
await waitReady();
});
it('has 64-byte signatures', (): void => {
const pair = sr25519PairFromSeed(randomAsU8a());

View File

@@ -3,8 +3,9 @@
import type { Keypair } from '../types.js';
import * as sr25519 from '@scure/sr25519';
import { u8aToU8a } from '@polkadot/util';
import { sr25519Sign as wasmSign } from '@polkadot/wasm-crypto';
/**
* @name sr25519Sign
@@ -17,5 +18,5 @@ export function sr25519Sign (message: string | Uint8Array, { publicKey, secretKe
throw new Error('Expected a valid secretKey, 64-bytes');
}
return wasmSign(publicKey, secretKey, u8aToU8a(message));
return sr25519.sign(secretKey, u8aToU8a(message));
}

View File

@@ -4,7 +4,6 @@
/// <reference types="@polkadot/dev-test/globals.d.ts" />
import { stringToU8a } from '@polkadot/util';
import { waitReady } from '@polkadot/wasm-crypto';
import { randomAsU8a } from '../random/asU8a.js';
import { sr25519PairFromSeed } from './pair/fromSeed.js';
@@ -14,10 +13,6 @@ import { sr25519Verify } from './verify.js';
const MESSAGE = stringToU8a('this is a message');
describe('verify', (): void => {
beforeEach(async (): Promise<void> => {
await waitReady();
});
it('can sign and verify a message', (): void => {
const pair = sr25519PairFromSeed(randomAsU8a());
const signature = sr25519Sign(MESSAGE, pair);

View File

@@ -1,8 +1,9 @@
// Copyright 2017-2025 @polkadot/util-crypto authors & contributors
// SPDX-License-Identifier: Apache-2.0
import * as sr25519 from '@scure/sr25519';
import { u8aToU8a } from '@polkadot/util';
import { sr25519Verify as wasmVerify } from '@polkadot/wasm-crypto';
/**
* @name sr25519Verify
@@ -18,5 +19,5 @@ export function sr25519Verify (message: string | Uint8Array, signature: string |
throw new Error(`Invalid signature, received ${signatureU8a.length} bytes, expected 64`);
}
return wasmVerify(signatureU8a, u8aToU8a(message), publicKeyU8a);
return sr25519.verify(u8aToU8a(message), signatureU8a, publicKeyU8a);
}

View File

@@ -3,8 +3,10 @@
import type { Keypair } from '../types.js';
import { randomBytes } from '@noble/hashes/utils';
import * as sr25519 from '@scure/sr25519';
import { u8aToU8a } from '@polkadot/util';
import { vrfSign } from '@polkadot/wasm-crypto';
const EMPTY_U8A = new Uint8Array();
@@ -17,5 +19,6 @@ export function sr25519VrfSign (message: string | Uint8Array, { secretKey }: Par
throw new Error('Invalid secretKey, expected 64-bytes');
}
return vrfSign(secretKey, u8aToU8a(context), u8aToU8a(message), u8aToU8a(extra));
return sr25519.vrf.sign(u8aToU8a(message), secretKey, u8aToU8a(context), u8aToU8a(extra), randomBytes);
// return vrfSign(secretKey, u8aToU8a(context), u8aToU8a(message), u8aToU8a(extra));
}

View File

@@ -4,7 +4,6 @@
/// <reference types="@polkadot/dev-test/globals.d.ts" />
import { stringToU8a, u8aEq } from '@polkadot/util';
import { waitReady } from '@polkadot/wasm-crypto';
import { randomAsU8a } from '../random/asU8a.js';
import { sr25519PairFromSeed } from './pair/fromSeed.js';
@@ -14,10 +13,6 @@ import { sr25519VrfVerify } from './vrfVerify.js';
const MESSAGE = stringToU8a('this is a message');
describe('vrf sign and verify', (): void => {
beforeEach(async (): Promise<void> => {
await waitReady();
});
it('has 96-byte proofs', (): void => {
const pair = sr25519PairFromSeed(randomAsU8a());

View File

@@ -1,8 +1,9 @@
// Copyright 2017-2025 @polkadot/util-crypto authors & contributors
// SPDX-License-Identifier: Apache-2.0
import * as sr25519 from '@scure/sr25519';
import { u8aToU8a } from '@polkadot/util';
import { vrfVerify } from '@polkadot/wasm-crypto';
const EMPTY_U8A = new Uint8Array();
@@ -20,5 +21,5 @@ export function sr25519VrfVerify (message: string | Uint8Array, signOutput: stri
throw new Error('Invalid vrfSign output, expected 96 bytes');
}
return vrfVerify(publicKeyU8a, u8aToU8a(context), u8aToU8a(message), u8aToU8a(extra), proofU8a);
return sr25519.vrf.verify(u8aToU8a(message), proofU8a, publicKeyU8a, u8aToU8a(context), u8aToU8a(extra));
}

View File

@@ -1313,6 +1313,15 @@ __metadata:
languageName: node
linkType: hard
"@noble/curves@npm:~1.9.2":
version: 1.9.7
resolution: "@noble/curves@npm:1.9.7"
dependencies:
"@noble/hashes": "npm:1.8.0"
checksum: 10/3cfe2735ea94972988ca9e217e0ebb2044372a7160b2079bf885da789492a6291fc8bf76ca3d8bf8dee477847ee2d6fac267d1e6c4f555054059f5e8c4865d44
languageName: node
linkType: hard
"@noble/hashes@npm:1.3.3, @noble/hashes@npm:^1.3.3":
version: 1.3.3
resolution: "@noble/hashes@npm:1.3.3"
@@ -1320,6 +1329,13 @@ __metadata:
languageName: node
linkType: hard
"@noble/hashes@npm:1.8.0, @noble/hashes@npm:~1.8.0":
version: 1.8.0
resolution: "@noble/hashes@npm:1.8.0"
checksum: 10/474b7f56bc6fb2d5b3a42132561e221b0ea4f91e590f4655312ca13667840896b34195e2b53b7f097ec080a1fdd3b58d902c2a8d0fbdf51d2e238b53808a177e
languageName: node
linkType: hard
"@nodelib/fs.scandir@npm:2.1.5":
version: 2.1.5
resolution: "@nodelib/fs.scandir@npm:2.1.5"
@@ -1638,6 +1654,7 @@ __metadata:
"@polkadot/x-bigint": "npm:13.5.9"
"@polkadot/x-randomvalues": "npm:13.5.9"
"@scure/base": "npm:^1.1.7"
"@scure/sr25519": "npm:^0.2.0"
tslib: "npm:^2.8.0"
peerDependencies:
"@polkadot/util": 13.5.9
@@ -2386,6 +2403,16 @@ __metadata:
languageName: node
linkType: hard
"@scure/sr25519@npm:^0.2.0":
version: 0.2.0
resolution: "@scure/sr25519@npm:0.2.0"
dependencies:
"@noble/curves": "npm:~1.9.2"
"@noble/hashes": "npm:~1.8.0"
checksum: 10/3c47b474811642b43fd8c96f7846c9d88c9a06eefa7d6360b6421ebdfb6cf582e1e8fdce9ae4708b088a0e323cd6519c883c3a33a284c2fad592414b02f19049
languageName: node
linkType: hard
"@sideway/address@npm:^4.1.4":
version: 4.1.4
resolution: "@sideway/address@npm:4.1.4"