Skip to content

Issue with ECDSA Derivation and recovery #2 #2016

@albertov19

Description

@albertov19

Hi again. Reopening #2006 as this is happening again:

import { ethers, Mnemonic, HDNodeWallet, Signature } from 'ethers';
import { Keyring } from '@polkadot/keyring';
import { stringToU8a, u8aToHex } from '@polkadot/util';
import { keccakAsU8a, cryptoWaitReady } from '@polkadot/util-crypto';

// ======= config =======
const MNEMONIC = process.env.MNEMONIC || 'hover message cause expand once spare impact unhappy risk lecture explain join';
const MESSAGE = 'Test';
const PRIMARY_PATH = "m/44'/60'/0'/0/0";
const LOOK_FOR = '0xDf43C58c3b353e273Fbf68b5FBBaA518e1Df7757'; // your "expected"
// ======================

async function deriveEthers(mnemonic, path) {
  const m = Mnemonic.fromPhrase(mnemonic);
  return HDNodeWallet.fromMnemonic(m, path);
}

function normalizeSigV(sigHex) {
  const b = Buffer.from(sigHex.slice(2), 'hex');
  if (b[64] === 0 || b[64] === 1) b[64] += 27;
  return '0x' + b.toString('hex');
}

(async () => {
  await cryptoWaitReady();

  // -------- derive on both SDKs (same mnemonic & path) --------
  const wallet = await deriveEthers(MNEMONIC, PRIMARY_PATH);
  const keyring = new Keyring();
  const pair = keyring.createFromUri(`${MNEMONIC}/${PRIMARY_PATH}`, undefined, 'ethereum', undefined, 2048);

  console.log('--- Derived account (primary path) ---');
  console.log('Path                 :', PRIMARY_PATH);
  console.log('Expected             :', LOOK_FOR);
  console.log('ethers address       :', wallet.address);
  console.log('polkadot.js address  :', pair.address);
  console.log('same?                :', wallet.address.toLowerCase() === pair.address.toLowerCase());
  console.log();

  // -------- sign RAW digest (no EIP-191), then recover --------
  const digest = keccakAsU8a(stringToU8a(MESSAGE)); // 32-byte keccak256("Test")
  const digestHex = ethers.hexlify(digest);

  const sigEthersStruct = await wallet.signingKey.sign(digestHex);
  const sigEthers = Signature.from(sigEthersStruct).serialized;

  const sigDot = normalizeSigV(u8aToHex(pair.sign(digest, { withType: false })));

  const recFromEthersSig = ethers.recoverAddress(digestHex, sigEthers);
  const recFromDotSig    = ethers.recoverAddress(digestHex, sigDot);

  console.log('--- Signing & recovery check ---');
  console.log('Message              :', MESSAGE);
  console.log('Digest               :', digestHex);
  console.log('ethers sig           :', sigEthers);
  console.log('dot sig              :', sigDot);
  console.log('rec(ethers sig)      :', recFromEthersSig);
  console.log('rec(dot sig)         :', recFromDotSig);
  console.log('==> matches derived? :', recFromEthersSig.toLowerCase() === wallet.address.toLowerCase()
                                       && recFromDotSig.toLowerCase() === wallet.address.toLowerCase());
  console.log();

  // -------- optional: search common paths/indexes for your EXPECTED address --------
  const commonRoots = [
    "m/44'/60'/0'/0",   // most wallets (account 0, change 0)
    "m/44'/60'/0'",     // some tools show account as the leaf
  ];
})();

Result:

--- Derived account (primary path) ---
Path                 : m/44'/60'/0'/0/0
Expected             : 0xDf43C58c3b353e273Fbf68b5FBBaA518e1Df7757
ethers address       : 0xDf43C58c3b353e273Fbf68b5FBBaA518e1Df7757
polkadot.js address  : 0xDf43C58c3b353e273Fbf68b5FBBaA518e1Df7757
same?                : true

--- Signing & recovery check ---
Message              : Test
Digest               : 0x85cc825a98ec217d960f113f5f80a95d7fd18e3725d37df428eb14f880bdfc12
ethers sig           : 0x7ebee7a1d35775aaf8a39c759676b380cfac887959205792c034e4e9e8ab4f7c5b0a23adbec207d833f8d78b29cc5a0d87b069a0f053b93f5b69460a9201d2ca1b
dot sig              : 0x89a7081374ddc746d4792d03cead8c0ac25b449fff8ee8884eb6f8bcbc51d99d0b1511d0a9c1a231e3a7e809c1be83cd19ff9b71174661d126a31818d19471361b
rec(ethers sig)      : 0xDf43C58c3b353e273Fbf68b5FBBaA518e1Df7757
rec(dot sig)         : 0x584F579Dd674CCa3c2c9C1EDD2A27725ee6f449e
==> matches derived? : false

Versions:

    "@polkadot/keyring": "14.0.1",
    "@polkadot/util": "14.0.1",
    "@polkadot/util-crypto": "14.0.1",
    "ethers": "^6.16.0",

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions