最近收到老板需求要用公钥生成批量的ETH地址,传统区块链钱包生成的钱包地址都是1个私钥对应该1个公钥和1个钱包地址,无法满足只提供公钥生成大量的钱包地址。在度娘的帮助下查看了相关的文档资料,拜读一些大牛的文章,通过HD钱包可以满足这个需求。

根据相关文档资料学习知道,HD钱包根据一个随机数种子通过分层确定性推导的方式得到n个私钥,这样保存的时候,只需要保存一个种子就可以了,这个推导过程确定也是单向的,子密钥不能推导出同层级的兄弟密钥,也不能推出父密钥。如果没有子链码也不能推导出孙密钥,这样简单安全保密性得到很大的提升;也可以用父公钥推导,需要三个参数:parent public key、parent chain code和index(要产生的是下一层的第几个child),然后就能够生成子私钥和子chain code,根据子私钥就能够得到子公钥,然后我们就能够继续使用子公钥、子chain code和index再生成下一层的key,可以不停地推导下去。

下面用js实现HD钱包的简单功能:

  • bip39:随机产生新的 助记词,并可以将其转成 binary 的 seed.
  • ethereumjs-wallet:产生和管理公私钥,创建 HD Wallet.
  • ethereumjs-util:集合许多 Ethereum 需要的运算功能.
  • ethereum-bip44:创建hdwallet,产生和管理公私钥
  • bitcore-lib:创建随机私钥Seed.
  • readline-sysnc:获取同步输入
  • 安装模块:npm i bip39 ethereumjs-wallet ethereumjs-util ethereum-bip44 bitcore-lib readline-sysnc --save.
/*HDwallet test---2022.07.26*/
var bip39 = require('bip39')
var hdkey = require('ethereumjs-wallet/hdkey')
var util = require('ethereumjs-util')
var fs = require('fs')
var readline = require('readline-sync')
var bitcore = require('bitcore-lib')
var EthereumBip44 = require('ethereum-bip44')
var map = {};

//输入生成地址方式(0:助记词,1:扩展公钥)
function choose_mode(){
while (1){
//选择生成钱包地址模式,0:用私钥生成,1:用公钥生成,2:使用公钥/私钥生成
var n = readline.question('pls input num for select the address generation mode (0:from_PrivateSeed,1: from_PublicSeed,2:from_ExtendedKey):');
if (n == 0){
let a = readline.question('pls input a number for select the private key generation mode (0:from_Mnemonic,1:from_Bitcore): ');
if (a == 0){
//使用BIP39助记词创建私钥
let key = from_Mnemonic();
from_PrivateSeed(key);
break;
}
else if (a == 1){
//使用Bitcore创建私钥
let key = from_Bitcore();
from_PrivateSeed(key);
break;
}
break;
}

else if (n == 1){
//输入扩展公钥,用扩展公钥产生address
let xkey = readline.question('pls input a XPublicExtendedkey:');

if (xkey.slice(0,4) !== 'xpub')
{
console.log('not an extended public key');
continue;
}
from_PublicSeed(xkey);
//from_ExtendedKey(xkey);
break;

}
else if (n == 2){
//使用扩展私钥/公钥生成钱包地址
let xkey = readline.question('pls input a Extendedkey(ExtendedPublicKey or ExtendedPrivateKey):');
from_ExtendedKey(xkey);
break;
}
}
}

choose_mode();

function from_Mnemonic(){
//使用BIP39生成助记词
let mnemonic = bip39.generateMnemonic();
console.log('Mnemonic: ',mnemonic);
//用助记词生成种子
let seed = bip39.mnemonicToSeed(mnemonic);
console.log('seed: ',seed.toString('hex'));
//用种子创建HD钱包
let hdWallet = hdkey.fromMasterSeed(seed);
//生成私钥
let privatekey = hdWallet.privateExtendedKey();
console.log('BIP32 Root Key: ',privatekey);
return privatekey;
}

function from_Bitcore(){
//用bitcore 生成私钥
let privatekey = new bitcore.HDPrivateKey();
privatekey = privatekey.toString();
console.log('BIP32 Root Key: ',privatekey);
return privatekey;
}

//用私钥生成HD钱包地址
function from_PrivateSeed(seed){
//用BIP44从私钥创建钱包
let wallet = EthereumBip44.fromPrivateSeed(seed);
//生成扩展公钥
let xpublickey = wallet.derive("m/44'/60'/0'/0").hdPublicKey;
console.log('BIP32 Extended Public Key: ',xpublickey.toString());
let address_index = readline.question('pls input a num for address_index:')
//不是数字退出
if (!Number.parseInt(address_index)){
console.log('pls input a num for address_index!');
return;
}

for (i = 0; i < address_index; i++){
let path = "m/44'/60'/0'/0/";
//获取地址
let address = wallet.getAddress(i);
address = util.toChecksumAddress(address.toString('hex'))
//获取私钥
let privatekey = wallet.getPrivateKey(i);
privatekey = '0x' + privatekey.toString('hex');
//获取公钥
let publickey = wallet.derive(path.concat(i.toString())).publicKey;
publickey = '0x' + publickey.toString('hex');
// console.log(privatekey);
// console.log(publickey);
let str = 'addr: ' + address.concat('; pubkey: ',publickey,'; prikey: ',privatekey)
//保存地址和公钥,私钥
let id = path.concat(i.toString());
map[id] = str;
}
}

function from_PublicSeed(seed){

//输入address_index
let address_index = readline.question('pls input address_index:');
if (!Number.parseInt(address_index)){
console.log('pls input a num for address_index!');
return;
}
//用公钥创建HD钱包
let wallet = EthereumBip44.fromPublicSeed(seed);
for (let i = 0; i < address_index; i++){
let address = wallet.getAddress(i);
address = util.toChecksumAddress(address.toString('hex'));
let bip44path = "m/44'/60'/0'/0/";
let id = bip44path.concat(i.toString());
map[id] = address;
}
}

//使用扩展私钥/扩展公钥生成ETH钱包地址
function from_ExtendedKey(Key){
//输入address_index
let address_index = readline.question('pls input address_index:');
if (!Number.parseInt(address_index)){
console.log('pls input a num for address_index!');
return;
}

let hdWallet = hdkey.fromExtendedKey(Key);
for (let i = 0; i < address_index; i++){
let key = hdWallet.deriveChild(i);
let address = util.pubToAddress(key._hdkey._publicKey,true);
address = util.toChecksumAddress(address.toString('hex'));
let bip44path = "m/44'/60'/0'/0/";
let id = bip44path.concat(i.toString());
//生成的address暂存到map中
map[id] = address;
}
}

//map转字符串
var str = JSON.stringify(map);

//去除不要的字符
str = str.replace(/\,/g,'\n').replace('{','').replace('}','').replace(/\"/g,'');
//打开文件
fs.open('test.txt','a',function(err,fd){
if (err){
throw err;
}

var data = str;
//写入文件
fs.write(fd, '\n'+data, 0, 'utf-8' ,function(err, written, string){
if (err){
throw err;
}
console.log('written successful!');
//console.log(written);
//console.log(string);
});

fs.close(fd,function(err){
if (err){
throw err;
}
console.log('file closed');
});
});

感谢相关大佬的资料,不到之处请多多指正。