@@ -6,6 +6,8 @@ import { ethers } from 'hardhat';
66
77import { SigningTests__factory } from '../typechain-types/factories/contracts/tests' ;
88import { SigningTests } from '../typechain-types/contracts/tests/SigningTests' ;
9+ import * as sr25519 from 'micro-sr25519' ;
10+ import { getBytes , hexlify , keccak256 } from 'ethers' ;
911
1012function randomBytesUnlike ( len : number , orig : Buffer ) : Buffer {
1113 do {
@@ -205,7 +207,107 @@ describe('Signing', function () {
205207 ) ;
206208 } ) ;
207209
208- // TODO: implement Sr25519
210+ it ( 'sr25519' , async ( ) => {
211+ // Try sr25519 (alg=6)
212+ // 32 byte context, empty message
213+ const sha256_kp = await se . testKeygen ( 6 , randomBytes ( 32 ) ) ;
214+ await testSignThenVerify (
215+ se ,
216+ 6 ,
217+ sha256_kp ,
218+ randomBytes ( 32 ) ,
219+ EMPTY_BUFFER ,
220+ 32 ,
221+ 0 ,
222+ ) ;
223+
224+ // Key derivation from polkadot test cases
225+ // See: https://github.com/polkadot-js/wasm/blob/10010830094e7d033bd11b16c5e3bc01a7045309/packages/wasm-crypto/src/rs/sr25519.rs#L176
226+ const secretSeed = getBytes (
227+ '0xfac7959dbfe72f052e5a0c3c8d6530f202b02fd8f9f5ca3580ec8deb7797479e' ,
228+ ) ;
229+ const secretKey = sr25519 . secretFromSeed ( secretSeed ) ;
230+ const publicKey = sr25519 . getPublicKey ( secretKey ) ;
231+ expect ( hexlify ( publicKey ) ) . eq (
232+ '0x46ebddef8cd9bb167dc30878d7113b7e168e6f0646beffd77d69d39bad76b47a' ,
233+ ) ;
234+
235+ // Known valid signature
236+ const msg = new TextEncoder ( ) . encode ( '<Bytes>message to sign</Bytes>' ) ;
237+ const sig = getBytes (
238+ '0x48ce2c90e08651adfc8ecef84e916f6d1bb51ebebd16150ee12df247841a5437951ea0f9d632ca165e6ab391532e75e701be6a1caa88c8a6bcca3511f55b4183' ,
239+ ) ;
240+ const sigSigner = getBytes (
241+ '0xf84d048da2ddae2d9d8fd6763f469566e8817a26114f39408de15547f6d47805' ,
242+ ) ;
243+
244+ // Verify JS implementation matches polkadot test case signature
245+ const isValid = sr25519 . verify ( msg , sig , sigSigner ) ;
246+ expect ( isValid ) . eq ( true ) ;
247+
248+ const CONTEXT = new TextEncoder ( ) . encode ( 'substrate' ) ;
249+
250+ // Verify on-chain implementation also works
251+ const result = await se . testVerify ( 6 , sigSigner , CONTEXT , msg , sig ) ;
252+ expect ( result ) . eq ( true ) ;
253+
254+ // Test key generation on-chian matches JS implementation
255+ const generatedKey = await se . testKeygen ( 6 , secretSeed ) ;
256+ expect ( hexlify ( getBytes ( generatedKey . secretKey ) . slice ( 0 , 64 ) ) ) . eq (
257+ hexlify ( secretKey ) ,
258+ ) ;
259+ expect ( generatedKey . publicKey ) . eq ( hexlify ( publicKey ) ) ;
260+
261+ // 64 byte secret, appended with 32 byte public key
262+ expect ( getBytes ( generatedKey . publicKey ) . length ) . eq ( 32 ) ;
263+ expect ( getBytes ( generatedKey . secretKey ) . length ) . eq ( 96 ) ;
264+ expect ( hexlify ( getBytes ( generatedKey . secretKey ) . slice ( 64 ) ) ) . eq (
265+ generatedKey . publicKey ,
266+ ) ;
267+
268+ // JS can verify on-chain signed message
269+ const onchainSigned = await se . testSign (
270+ 6 ,
271+ generatedKey . secretKey ,
272+ CONTEXT ,
273+ msg ,
274+ ) ;
275+ const jsVerify = sr25519 . verify (
276+ msg ,
277+ getBytes ( onchainSigned ) ,
278+ getBytes ( generatedKey . publicKey ) ,
279+ ) ;
280+ expect ( jsVerify ) . eq ( true ) ;
281+ // And on-chain can verify on-chain signed message
282+ expect (
283+ await se . testVerify (
284+ 6 ,
285+ generatedKey . publicKey ,
286+ CONTEXT ,
287+ msg ,
288+ onchainSigned ,
289+ ) ,
290+ ) . eq ( true ) ;
291+
292+ // JS roundtrip with on-chain generated keypair
293+ const jsSigned = sr25519 . sign (
294+ getBytes ( generatedKey . secretKey ) . slice ( 0 , 64 ) ,
295+ msg ,
296+ ) ;
297+ expect ( sr25519 . verify ( msg , jsSigned , getBytes ( generatedKey . publicKey ) ) ) . eq (
298+ true ,
299+ ) ;
300+
301+ // on-chain verify JS signed message
302+ const onchainVerify = await se . testVerify (
303+ 6 ,
304+ generatedKey . publicKey ,
305+ CONTEXT ,
306+ msg ,
307+ jsSigned ,
308+ ) ;
309+ expect ( onchainVerify ) . eq ( true ) ;
310+ } ) ;
209311
210312 it ( 'Secp256r1 (Prehashed SHA256)' , async ( ) => {
211313 // Try Secp256r1 (alg=7)
0 commit comments