Closed3

【16倍高速化】ethers.jsで高速にランダムなアドレスを大量に生成するTips

ピン留めされたアイテム
serinuntiusserinuntius

Xでもっといいアイデアを頂いたので掲載させていただく。

araさんありがとうございました!

https://x.com/arandoros/status/1718231242372694316?s=20
https://x.com/arandoros/status/1718231982319263774?s=20
ただし秘密鍵は不要で適当なアドレスがあればいいっていう場合のみに使えるという制約がつく。
自分のは秘密鍵も欲しくなったら、同等の速度で動くはず。

❯ yarn generateAddresses
yarn run v1.22.19
$ tsx scripts/generateAddresses.ts
10000 個のアドレスが addresses.csv ファイルに保存されました。
✨  Done in 0.22s.

早すぎる!

serinuntiusserinuntius

テスト用にアドレスを大量に作りたい

2023/10/28現在、GPT4にethers.jsでアドレスを大量に生成したいっていうと、次のようなコードを吐いてくる。

import { Wallet } from 'ethers';
import * as fs from 'fs';

const generateAddresses = async (): Promise<void> => {
  let csvContent = 'address\n'; // CSV Header

  for (let i = 0; i < 10000; i++) {
    const wallet = Wallet.createRandom();
    csvContent += `${wallet.address}\n`;
  }

  // Write to CSV file
  fs.writeFile('addresses.csv', csvContent, (err) => {
    if (err) {
      console.error('エラーが発生:', err);
    } else {
      console.log('10,000 個のアドレスが addresses.csv ファイルに保存されました。');
    }
  });
};

generateAddresses();

実行してみる

❯ yarn generateAddresses
yarn run v1.22.19
$ tsx scripts/generateAddresses.ts
10,000 個のアドレスが addresses.csv ファイルに保存されました。
✨  Done in 96.70s.

間違いではないけど、ただアドレスが1万個欲しいにしてはやりすぎだし、遅すぎる。

手元のM1 Macで実行すると90秒近くかかる。

ちゃんとベンチ取ったわけじゃないけど、csprng(暗号論的擬似乱数生成器)で何個も生成するのが遅いんだと思う。

高速化する

何個もseedを生成する必要は今回のユースケースにおいて全く必要なく、HDNodeをDeriveしていくだけでいい。

import {Wallet} from 'ethers';
import {HDNode} from 'ethers/lib/utils';
import {promises as fs} from 'fs';

const generateAddresses = async (): Promise<void> => {
  // ランダムなウォレットとその mnemonic を生成
  const masterWallet = Wallet.createRandom();
  const masterNode = HDNode.fromMnemonic(masterWallet.mnemonic.phrase);

  const numAddresses = 10000;
  const addresses = [];

  // 基本のパスを設定
  const baseNode = masterNode.derivePath("m/44'/60'/0'/0");

  for (let i = 0; i < numAddresses; i++) {
    const derivedNode = baseNode.derivePath(String(i));
    addresses.push(derivedNode.address);
  }

  // CSV形式に変換
  const csvContent = `address\n${addresses.join('\n')}\n`;

  // ファイルに書き込み
  await fs.writeFile('addresses.csv', csvContent);

  console.log(
    `${numAddresses} 個のアドレスが addresses.csv ファイルに保存されました。`
  );
};

generateAddresses().catch(err => {
  console.error('エラーが発生:', err);
});

実行してみる

❯ yarn generateAddresses
yarn run v1.22.19
$ tsx scripts/generateAddresses.ts
10000 個のアドレスが addresses.csv ファイルに保存されました。
✨  Done in 6.34s.

やったぜ!!16倍もの高速化に成功した🚀🚀🚀

もっと高速化しようと思うと、そもそもJSを使わないとか、viem使うとかそういうアイデアになってきそう。

↓下でviemも試してみました。viemは早いというイメージがありましたが、ethers.js高速化の3倍以上遅いです。

他にいいやり方あればツッコミお願いします!

serinuntiusserinuntius

viemも試してみた

import {promises as fs} from 'fs';
import {
  hdKeyToAccount,
  generateMnemonic,
  english,
  mnemonicToAccount,
} from 'viem/accounts';

const generateAddresses = async (): Promise<void> => {
  const mnemonic = generateMnemonic(english);
  const account = mnemonicToAccount(mnemonic);

  const baseHDKey = account.getHdKey();

  const numAddresses = 10000;
  const addresses = [];

  for (let i = 0; i < numAddresses; i++) {
    const derivedHDKey = baseHDKey.deriveChild(i);
    const account = hdKeyToAccount(derivedHDKey);
    addresses.push(account.address);
  }

  // CSV形式に変換
  const csvContent = `address\n${addresses.join('\n')}\n`;

  // ファイルに書き込み
  await fs.writeFile('addresses.csv', csvContent);

  console.log(
    `${numAddresses} 個のアドレスが addresses.csv ファイルに保存されました。`
  );
};

generateAddresses().catch(err => {
  console.error('エラーが発生:', err);
});

実行!!

yarn generateAddresses
yarn run v1.22.19
$ tsx scripts/generateAddresses.ts
10000 個のアドレスが addresses.csv ファイルに保存されました。
✨  Done in 19.43s.

!!!!!!!!!!!!

まさかのviemの方が遅いです

このスクラップは6ヶ月前にクローズされました