前段時間做了一下eth錢包開發(fā)的事情,現(xiàn)在項目基本結束了宙搬∪牡猓空下來就去梳理一下之前遇到的一些關鍵問題目尖,以及踩過的一些坑。
下面就和大家分享一下我遇到過的一個問題扎运。
創(chuàng)建錢包
在錢包開發(fā)中瑟曲,創(chuàng)建錢包大致分為以下幾個步驟。
1豪治、隨機生成一組助記詞
2洞拨、根據(jù)助記詞生成種子 seed
3、根據(jù)種子seed生成ECKeyPair
4负拟、根據(jù)ECKeyPair和密碼創(chuàng)建錢包
我發(fā)現(xiàn)網(wǎng)上很多博客在生成ECKeyPair的時候烦衣,姿勢不對。
網(wǎng)上很多教程的生成方式
ECKeyPair ecKeyPair= ECKeyPair.create(sha256(seed));
這種方式是Web3j
提供的API齿椅,看上去是很簡潔琉挖,用起來很方便,但實際上坑很大涣脚,根本不能在項目中使用示辈,只能作為Demo。
在創(chuàng)建ETH錢包的過程遣蚀,需要使用BIP32
,BIP39
,BIP44
,3個協(xié)議矾麻。它們的作用如下:
BIP32:
定義了目前被廣泛使用的 HD Wallet,生成種子seed 和公鑰芭梯、私鑰的险耀。
BIP39:
用于生成助記詞
BIP44:
基于 BIP32 的系統(tǒng),賦予樹狀結構中的各層特殊的意義玖喘。讓同一個 seed 可以支援多幣種甩牺、多帳戶等。簡單一點說累奈,它就是指定幣種規(guī)范的一個協(xié)議贬派。它的格式如下:
m / purpose' / coin_type' / account' / change / address_index
其中的 purporse'
固定是 44'
急但,代表使用 BIP44。而coin_type'
用來表示不同幣種搞乏,例如 Bitcoin 就是 0'
波桩,Ethereum 是 60'
目前市面上的Ethereum 錢包均采用以上 Bitcoin HD Wallet 的架構,用的都是BIP44:
请敦,具體是這樣的m/44'/60'/0'/0/0
而Web3j那種創(chuàng)建方式镐躲,并沒有采用BIP44
,在項目中是不能采用這種方式的侍筛,但是依然被很多人采用萤皂,并且在網(wǎng)上傳播。
下面是我驗證這種方式的一個例子勾笆。
private void test(String password, String mnemonics) {
String path = mContext.getCacheDir() + "/MyWallet";
//2種方式生成seed是一樣的
// byte[] seed = MnemonicUtils.generateSeed(mnemonics, "");
// ECKeyPair ecKeyPair = ECKeyPair.create(sha256(seed));
byte[] seed2 = new SeedCalculator().calculateSeed(mnemonics, "");
ECKeyPair ecKeyPair2 = ECKeyPair.create(sha256(seed2));
Log.i(TAG, "test2 pubk : " + ecKeyPair2.getPublicKey());
Log.i(TAG, "test2 prek : " + ecKeyPair2.getPrivateKey());
Log.i(TAG, "test2 address : " + Keys.getAddress(ecKeyPair2));
}
通過seed獲取ECKeyPair敌蚜,然后通過ECKeyPair獲取公鑰、私鑰窝爪、地址。
結果是這樣的齐媒,可以明顯看出是有問題的蒲每。
PublicKey:
7244922113023370429973158797216818288186973573283250245018732882291753635193658294263389808990758395488598426777669619093953194711324283066023497720888144
privateKey:
51826596792659989115758568077400059679039347198031504935670220417030436567903
address :
f81f6c5eb74f1b726e4bce7f4528c966e46dc2e3
注意:明文私鑰是64位的。
正確的姿勢
1喻括、生成 seed
2邀杏、生成 master key
3、生成 child key
4唬血、我們?nèi)〉谝唤Mchild key即m/44'/60'/0'/0/0 得到私鑰,keystore及地址
完整代碼如下望蜡,這段代碼用到了BIP32
,BIP39
,BIP44
/**
* generate key pair to create eth wallet_normal
* 生成KeyPair , 用于創(chuàng)建錢包(助記詞生成私鑰)
*/
public ECKeyPair generateKeyPair(String mnemonics) {
// 1. we just need eth wallet_normal for now
AddressIndex addressIndex = BIP44
.m()
.purpose44()
.coinType(60)
.account(0)
.external()
.address(0);
// 2. calculate seed from mnemonics , then get master/root key ; Note that the bip39 passphrase we set "" for common
byte[] seed = new SeedCalculator().calculateSeed(mnemonics, "");
ExtendedPrivateKey rootKey = ExtendedPrivateKey.fromSeed(seed, Bitcoin.MAIN_NET);
Log.i(TAG, "mnemonics:" + mnemonics);
String extendedBase58 = rootKey.extendedBase58();
Log.i(TAG, "extendedBase58:" + extendedBase58);
// 3. get child private key deriving from master/root key
ExtendedPrivateKey childPrivateKey = rootKey.derive(addressIndex, AddressIndex.DERIVATION);
String childExtendedBase58 = childPrivateKey.extendedBase58();
Log.i(TAG, "childExtendedBase58:" + childExtendedBase58);
// 4. get key pair
byte[] privateKeyBytes = childPrivateKey.getKey();
ECKeyPair keyPair = ECKeyPair.create(privateKeyBytes);
// we 've gotten what we need
String privateKey = childPrivateKey.getPrivateKey();
String publicKey = childPrivateKey.neuter().getPublicKey();
String address = Keys.getAddress(keyPair);
Log.i(TAG, "privateKey:" + privateKey);
Log.i(TAG, "publicKey:" + publicKey);
Log.i(TAG, "address:" + Constant.PREFIX_16 + address);
return keyPair;
}