以太坊钱包1-Android-创建钱包
以太坊錢(qián)包1-Android-創(chuàng)建錢(qián)包
2018年09月19日 18:24:13?勤奮的懶惰?閱讀數(shù) 1557
?版權(quán)聲明:本文為博主原創(chuàng)文章,未經(jīng)博主允許不得轉(zhuǎn)載。 https://blog.csdn.net/u012846783/article/details/82775643
前言
會(huì)按照如下順序?qū)懘a
1.簡(jiǎn)單創(chuàng)建錢(qián)包
?? ?a.導(dǎo)出助記詞、keystore、私鑰。
?? ?b.導(dǎo)入助記詞、keystore、私鑰。
2.轉(zhuǎn)賬(以太幣)
?? ?a.賬號(hào)直接互轉(zhuǎn)以太幣
?? ?b.查詢余額
3.以太坊鏈上代幣(Token)互轉(zhuǎn)
?? ?a.在以太坊測(cè)試鏈上部署Token的智能合約
?? ?b.賬號(hào)直接互轉(zhuǎn)代幣
4.開(kāi)發(fā)pc端控制臺(tái)錢(qián)包程序
簡(jiǎn)單創(chuàng)建錢(qián)包
1.簡(jiǎn)單的概念
a.什么是區(qū)塊鏈?
? ? ? ? ? ? ?分布式數(shù)據(jù)庫(kù),用于存儲(chǔ)數(shù)據(jù)。存儲(chǔ)的數(shù)據(jù)放在一個(gè)個(gè)區(qū)塊中,下一個(gè)區(qū)塊總是指向上一個(gè)區(qū)塊,就像項(xiàng)鏈一個(gè)圈著一個(gè),所以把這個(gè)由區(qū)塊圈起來(lái)的鏈撐作區(qū)塊鏈。
b.什么是區(qū)塊
? ? ? ? ? ? ??區(qū)塊里面存儲(chǔ)具體數(shù)據(jù),包含區(qū)塊頭和區(qū)塊體。
? ? ? ? ? ? ? 區(qū)塊頭包含上一個(gè)區(qū)塊的hash值等等
? ? ? ? ? ? ? 區(qū)塊體包含具體的交易數(shù)據(jù)
c.錢(qián)包
? ? ? ? ? ? ? 錢(qián)包用來(lái)存儲(chǔ)和使用數(shù)字貨幣的工具。
? ? ? ? ? ? ? 錢(qián)包地址:類似銀行卡卡號(hào)。由錢(qián)包公鑰推導(dǎo)出來(lái)
? ? ? ? ? ? ? 錢(qián)包公鑰:錢(qián)包私鑰推導(dǎo)出來(lái)的。
? ? ? ? ? ? ??錢(qián)包私鑰:類似銀行卡密碼。
? ? ? ? ? ? ? 私鑰===推導(dǎo)》===公鑰===推導(dǎo)》錢(qián)包地址(整個(gè)過(guò)程不可逆,也就是說(shuō)拿到錢(qián)包地址并不能推導(dǎo)出公鑰)
d.bip32,bip39,bip44
? ? ? ? ? ? ??bip(Bitcoin Improvement Proposals:比特幣改進(jìn)建議)
? ? ? ? ? ? ??bip32:定義了分層確定性錢(qián)包(HD Wallet),通過(guò)一個(gè)種子(seed)可以生成多個(gè)keypairs(公鑰和私鑰)樹(shù)狀結(jié)構(gòu)
? ? ? ? ? ? ??bip39:用簡(jiǎn)單易記的單詞代替seed,單詞總列表為2048個(gè)單詞組成,這些單詞稱之為助記詞助記詞列表有多中語(yǔ)言版本:比如英語(yǔ),中文簡(jiǎn)體,中文繁體等。具體地址:
? ? ? ? ? ? ??bip44:基于 BIP32 ,通過(guò)賦予樹(shù)狀結(jié)構(gòu)各層特殊含義,讓同一個(gè) seed 可以支援多幣種、多帳戶等
? ? ? ? ? ? ? ? ? ? ? ? ? m / purpose' / coin_type' / account' / change / address_index
? ? ? ? ? ? ? ? ? ? ? ? ? //purporse': 固定值44', 代表是BIP44
? ? ? ? ? ? ? ? ? ? ? ? ? //coin_type': 這個(gè)代表的是幣種, 可以兼容很多種幣, 比如BTC是0', ETH是60'
? ? ? ? ? ??? ? ? ? ? ? ? //btc一般是 m/44'/0'/0'/0
? ? ? ? ? ? ? ? ? ? ? ? ? //eth一般是 m/44'/60'/0'/0
e.助記詞
? ? ? ? ? ? ? 因?yàn)檫^(guò)長(zhǎng)的seed無(wú)法讓人民容易的記住,就使用助記詞。是在bip39規(guī)則中提出來(lái)的。?一般使用英語(yǔ)版本,通用。
?f.keystore?
? ? ? ? ? ? ? keystore =?私鑰 + 密碼生成。本質(zhì)是一段json字符串。
2.前置條件
a.需要用到兩個(gè)庫(kù):
? ? ? ? ? ? ??web3j:Lightweight Java library for integration with Ethereum clients;
?? ??? ??? ??? ?主要使用它來(lái)生成助記詞和seed,后續(xù)需要用此庫(kù)來(lái)進(jìn)行交易(轉(zhuǎn)賬,部署合約,加載合約等)
?? ??? ??? ???bitcoinj:通過(guò)前面生成的seed生成支持bip32和bip44的私鑰。(因?yàn)閣eb3j不支持生成bip44的錢(qián)包,而市面上多使用bip32,bip39,bip44標(biāo)準(zhǔn)結(jié)合生成的錢(qián)包,所以引用此包。同時(shí)還有其他的庫(kù)支持bip32和bip44.比如:Nova Crypto的系列包但是他的bip32庫(kù)有些問(wèn)題。這篇文章使用的就是Nova Crypto系列生成的錢(qián)包。)
?? ??? ??? ??? ?關(guān)于使用bip32,39,44去生成一個(gè)錢(qián)包的具體的算法我并沒(méi)有弄懂。比如:一個(gè)種子如何生成多個(gè)鑰匙對(duì),如何生成助記詞,生成keystore的算法等等我都沒(méi)有具體去看,不過(guò)還是建議看看大概的流程。
2.引用庫(kù):
?? ??? ??? ?web3j:
bitcoinj:? ?
implementation 'org.bitcoinj:bitcoinj-core:0.14.7'3.生成bip39錢(qián)包(web3j直接有工具類支持)
a.直接調(diào)用WalletUtils.generateBip39Wallet()
? ? ? ? ? ? ? 生成一個(gè)bip39的錢(qián)包。方法詳細(xì)代碼如下:
?
/**
?? ??? ? ? ? * 生成bip39 錢(qián)包
?? ??? ? ? ? * @param password 生成keystore用的密碼,也是用于助記詞生成種子所加的鹽。
?? ??? ? ? ? * @param destinationDirectory keystore保存的目錄
?? ??? ? ? ? * @return
?? ??? ? ? ? * @throws CipherException
?? ??? ? ? ? * @throws IOException
?? ??? ? ? ? */
?? ??? ??? ?public static Bip39Wallet generateBip39Wallet(String password, File destinationDirectory)
?? ? ? ? ? ? ? ?throws CipherException, IOException {
?? ? ? ? ? ? ? ?? ?//生成標(biāo)準(zhǔn)的128位的熵,因?yàn)?byte占8位,所需要16byte。https://www.jianshu.com/p/e6a4150eb729
?? ??? ? ? ? ? ?byte[] initialEntropy = new byte[16];
?? ??? ? ? ? ? ?secureRandom.nextBytes(initialEntropy);
?? ??? ? ? ? ? ?//通過(guò)熵生成助記詞
?? ??? ? ? ? ? ?String mnemonic = MnemonicUtils.generateMnemonic(initialEntropy);
?? ??? ? ? ? ? ?//通過(guò)助記詞生成seed(種子),這里填入了password作為鹽,這樣會(huì)導(dǎo)致生成seed不夠通用,
?? ??? ? ? ? ? ?//無(wú)法再其他的錢(qián)包(imToken等)導(dǎo)入。所以建議設(shè)置為null。
?? ??? ? ? ? ? ?byte[] seed = MnemonicUtils.generateSeed(mnemonic, password);
?? ??? ? ? ? ? ?//生成公私鑰對(duì)(bip32,bip44規(guī)則采用的生成方式不同)
?? ??? ? ? ? ? ?ECKeyPair privateKey = ECKeyPair.create(sha256(seed));
?? ??? ? ? ? ? ?//生成WalletFile,也就是keystore的實(shí)體類,把WalletFile變?yōu)閖son對(duì)象就是keystore
?? ??? ? ? ? ? ?String walletFile = generateWalletFile(password, privateKey, destinationDirectory, false);
?? ??? ? ? ? ? ?return new Bip39Wallet(walletFile, mnemonic);
?? ? ? ??? ?}
?? ? ? ??? ?public static String generateWalletFile(
?? ? ? ? ? ? ? ?String password, ECKeyPair ecKeyPair, File destinationDirectory, boolean useFullScrypt)
?? ? ? ? ? ? ? ?throws CipherException, IOException {
?? ??? ? ? ? ? ?WalletFile walletFile;
?? ??? ? ? ? ? ?if (useFullScrypt) {
?? ??? ? ? ? ? ??? ?//生成標(biāo)準(zhǔn)錢(qián)包(此處在android中使用一般會(huì)報(bào)oom,跟算法有關(guān)。在web3j的issus中能找到相關(guān)的內(nèi)容,
?? ??? ? ? ? ? ??? ?//一般android中使用的是生成輕錢(qián)包,用下面方法)
?? ??? ? ? ? ? ? ? ?walletFile = Wallet.createStandard(password, ecKeyPair);
?? ??? ? ? ? ? ?} else {
?? ??? ? ? ? ? ??? ?//生成輕錢(qián)包 一般android使用此生成方式
?? ??? ? ? ? ? ? ? ?walletFile = Wallet.createLight(password, ecKeyPair);
?? ??? ? ? ? ? ?}
?? ??? ? ? ? ? ?//keystore文件名
?? ??? ? ? ? ? ?String fileName = getWalletFileName(walletFile);
?? ??? ? ? ? ? ?File destination = new File(destinationDirectory, fileName);
?? ??? ? ? ? ? ?//將keystore寫(xiě)入本地文件
?? ??? ? ? ? ? ?objectMapper.writeValue(destination, walletFile);
?? ??? ? ? ? ? ?return fileName;
?? ??? ? ? ?}
2.使用web3j的注意事項(xiàng)。
? ? ? ? ?(web3j分android版本和java版本。)在使用上述生成bip39錢(qián)包的時(shí)候有幾個(gè)注意的點(diǎn)。
? ? ? ? ? ??MnemonicUtils
?? ??? ??? ??? ?在使用web3j生成助記詞的時(shí)候需要使用到MnemonicUtils,如果使用庫(kù)里面生成助記詞的MnemonicUtils.generateMnemonic()方法,則爆出下標(biāo)越界的錯(cuò)誤:java.lang.IndexOutOfBoundsException: Index: 959
?? ??? ??? ??? ?由于此工具類讀取助記詞列表的代碼有問(wèn)題,則需要替換成java版本,或者將助記詞列表放置到assets里面自己讀取。我選擇將MnemonicUtils整個(gè)代碼copy一份將讀取助記詞列表的代碼替換成java版本,這樣的做法是在web3j的issus里面找到的解決方法。具體替換MnemonicUtils方法為:
/**
?? ??? ??? ? ? ? * 使用java的方式讀取助記詞列表
?? ??? ??? ? ? ? * @return 助記詞列表
?? ??? ??? ? ? ? *
?? ??? ??? ? ? ? * @notice 如果此處出錯(cuò)則將en-mnemonic-word-list.txt放入項(xiàng)目assets文件進(jìn)行讀取
?? ??? ??? ? ? ? */
?? ??? ??? ? ? ?private static List<String> populateWordList() {
?? ??? ??? ? ? ? ? ?InputStream inputStream = Thread.currentThread().getContextClassLoader()
?? ??? ??? ? ? ? ? ? ? ? ? ?.getResourceAsStream("en-mnemonic-word-list.txt");
?? ??? ??? ? ? ? ? ?try {
?? ??? ??? ? ? ? ? ? ? ?return readAllLines(inputStream);
?? ??? ??? ? ? ? ? ?} catch (Exception e) {
?? ??? ??? ? ? ? ? ? ? ?return Collections.emptyList();
?? ??? ??? ? ? ? ? ?}
?? ??? ??? ? ? ?}
?? ??? ??? ? ? ?public static List<String> readAllLines(InputStream inputStream) throws IOException {
?? ??? ??? ? ? ? ? ?BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
?? ??? ??? ? ? ? ? ?List<String> data = new ArrayList<>();
?? ??? ??? ? ? ? ? ?for (String line; (line = br.readLine()) != null; ) {
?? ??? ??? ? ? ? ? ? ? ?data.add(line);
?? ??? ??? ? ? ? ? ?}
?? ??? ??? ? ? ? ? ?return data;
?? ??? ??? ? ? ?}
?? ??? ??? ?WalletUtils
?? ??? ??? ??? ?如果使用自己修改過(guò)的MnemonicUtils,則需要WalletUtils也替換。使用WalletUtils.generateBip39Wallet()方法時(shí),里面用到了secureRandom,這個(gè)是SecureRandomUtils.secureRandom()生成的因?yàn)镾ecureRandomUtils是缺省的,所以在重寫(xiě)的WalletUtils無(wú)法引用,所以在使用secureRandom的地方直接使用系統(tǒng)的SecureRandom即可。這些代碼很簡(jiǎn)單,看一眼代碼一清二楚。
4.生成符合bip32、bip39、bip44的錢(qián)包
具體代碼? ? ?
public static BaibeiWallet generateBip44Wallet(String password)
? ? ? ? ? ? throws CipherException, IOException {
?? ? ? ? ? ?//1.通過(guò)bip39生成助記詞
?? ? ? ? ? ?byte[] initialEntropy = new byte[16];
?? ? ? ? ? ?secureRandom.nextBytes(initialEntropy);
?? ? ? ? ? ?String mnemonic = MnemonicUtils.generateMnemonic(initialEntropy);
?? ? ? ? ? ?if (BuildConfig.DEBUG) {
?? ? ? ? ? ? ? ?Log.i("TAG", "generateBip44Wallet: 助記詞 = " + mnemonic);
?? ? ? ? ? ?}
?? ? ? ? ? ?//2.生成種子
?? ? ? ? ? ?byte[] seed = MnemonicUtils.generateSeed(mnemonic, null);
?? ? ? ? ? ?//3. 生成根私鑰 root private key 樹(shù)頂點(diǎn)的master key ;bip32
?? ? ? ? ? ?DeterministicKey rootPrivateKey = HDKeyDerivation.createMasterPrivateKey(seed);
?? ? ? ? ? ?if (BuildConfig.DEBUG) {
?? ? ? ? ? ? ? ?// 根私鑰進(jìn)行 priB58編碼,得到測(cè)試網(wǎng)站上顯示的數(shù)據(jù) https://iancoleman.io/bip39/
?? ? ? ? ? ? ? ?NetworkParameters params = MainNetParams.get();
?? ? ? ? ? ? ? ?String priv = rootPrivateKey.serializePrivB58(params);
?? ? ? ? ? ? ? ?Log.i("TAG", "BIP32 Extended Private Key:" + priv);
?? ? ? ? ? ?}
?? ? ? ? ? ?// 4. 由根私鑰生成 第一個(gè)HD 錢(qián)包
?? ? ? ? ? ?DeterministicHierarchy dh = new DeterministicHierarchy(rootPrivateKey);
?? ? ? ? ? ?// 5. 定義父路徑 H則是加強(qiáng) imtoken中的eth錢(qián)包進(jìn)過(guò)測(cè)試發(fā)現(xiàn)使用的是此方式生成 bip44
?? ? ? ? ? ?List<ChildNumber> parentPath = HDUtils.parsePath("M/44H/60H/0H/0");
?? ? ? ? ? ?// 6. 由父路徑,派生出第一個(gè)子私鑰 "new ChildNumber(0)" 表示第一個(gè) (m/44'/60'/0'/0/0)
?? ? ? ? ? ?DeterministicKey child = dh.deriveChild(parentPath, true, true, new ChildNumber(0));
?? ? ? ? ? ?byte[] privateKeyByte = child.getPrivKeyBytes();
?? ? ? ? ? ?//7.通過(guò)私鑰生成公私鑰對(duì)
?? ? ? ? ? ?ECKeyPair keyPair = ECKeyPair.create(privateKeyByte);
?? ? ? ? ? ?Log.i("TAG", "generateBip44Wallet: 鑰匙對(duì) ?私鑰 = " + Numeric.toHexStringNoPrefix(keyPair.getPrivateKey()));
?? ? ? ? ? ?Log.i("TAG", "generateBip44Wallet: 鑰匙對(duì) ?公鑰 = " + Numeric.toHexStringNoPrefix(keyPair.getPublicKey()));
?? ? ? ? ? ?//8.通過(guò)密碼和鑰匙對(duì)生成WalletFile也就是keystore的bean類
?? ? ? ? ? ?WalletFile walletFile = Wallet.createLight(password, keyPair);
?? ? ? ? ? ?//Keys.toChecksumAddress 是為了更好的顯示地址降低輸錯(cuò)概率。方法里面有eip55可以查看下
?? ? ? ? ? ?Log.i("TAG", "generateBip44Wallet: 地址 = " + Keys.toChecksumAddress(walletFile.getAddress()));
?? ? ? ? ? ?return new BaibeiWallet(walletFile, mnemonic,keyPair);
? ??? ??? ?}
上面的BaibeiWallet是自己定義的一個(gè)錢(qián)包類,里面就放置了WalletFile,助記詞和鑰匙對(duì)。簡(jiǎn)單測(cè)試使用。
public class BaibeiWallet extends Bip39Wallet {
?? ??? ? ? ?WalletFile walletFile;
?? ??? ? ? ?ECKeyPair mKeyPair;
?? ??? ? ? ?public BaibeiWallet(WalletFile walletFile,String mnemonic) {
?? ??? ? ? ? ? ?super(null,mnemonic);
?? ??? ? ? ? ? ?this.walletFile = walletFile;
?? ??? ? ? ?}
?? ??? ? ? ?public BaibeiWallet( WalletFile walletFile,String mnemonic, ECKeyPair mKeyPair) {
?? ??? ? ? ? ? ?super(null, mnemonic);
?? ??? ? ? ? ? ?this.walletFile = walletFile;
?? ??? ? ? ? ? ?this.mKeyPair = mKeyPair;
?? ??? ? ? ?}
?? ??? ? ? ?public BaibeiWallet(String filename, String mnemonic) {
?? ??? ? ? ? ? ?super(filename, mnemonic);
?? ??? ? ? ?}
?? ??? ? ? ?public ECKeyPair getKeyPair() {
?? ??? ? ? ? ? ?return mKeyPair;
?? ??? ? ? ?}
?? ??? ? ? ?public void setKeyPair(ECKeyPair keyPair) {
?? ??? ? ? ? ? ?this.mKeyPair = keyPair;
?? ??? ? ? ?}
?? ??? ? ? ?public WalletFile getWalletFile() {
?? ??? ? ? ? ? ?return walletFile;
?? ??? ? ? ?}
?? ??? ? ? ?public void setWalletFile(WalletFile walletFile) {
?? ??? ? ? ? ? ?this.walletFile = walletFile;
?? ??? ? ? ?}
?? ??? ? ? ?public String getKeyStoreJsonString(){
?? ??? ? ? ? ? ?Gson gson = new Gson();
?? ??? ? ? ? ? ?JSONObject j = null;
?? ??? ? ? ? ? ?try {
?? ??? ? ? ? ? ? ? ?j = new JSONObject(gson.toJson(walletFile));
?? ??? ? ? ? ? ? ? ?//j.remove("address");
?? ??? ? ? ? ? ?} catch (JSONException e) {
?? ??? ? ? ? ? ? ? ?e.printStackTrace();
?? ??? ? ? ? ? ?}
?? ??? ? ? ? ? ?return j.toString();
?? ??? ? ? ?}
?? ??? ?}
e.導(dǎo)出助記詞、keystore、私鑰
1.導(dǎo)出助記詞
? ? ? ? ? ? ? 即把前面生成mnemonic展示給用戶看一下。一般在導(dǎo)出之前都會(huì)校驗(yàn)一遍密碼。在Wallet里面有一個(gè)解密方法:? ? ?
? ?第一個(gè)參數(shù)是密碼,第二個(gè)參數(shù)是walletFile。這個(gè)walletFile在前面的錢(qián)包BaibeiWallet中有存儲(chǔ),返回一個(gè)ECKeyPair。如果方法沒(méi)有拋出CipherException異常則表示解密成功,也就是可以把錢(qián)包BaibeiWallet中的mnemonic字段展示給用戶。
? ??? ??? ?2.導(dǎo)出keystore
? ? ? ? ? ? ? 同理調(diào)用Wallet.decrypt(password, walletFile);解密成功則把BaibeiWallet中的walletFile變成json字符串導(dǎo)出給用戶。至于只是展示還是需要存儲(chǔ)則看自己需求了,這里僅僅只是展示。
? ??? ??? ?3.導(dǎo)出私鑰
? ??? ??? ??? ?同理調(diào)用Wallet.decrypt(password, walletFile);解密成功則把返回的ECKeyPair中的私鑰轉(zhuǎn)換為16進(jìn)制導(dǎo)出? ? ? ? ? ??
f.導(dǎo)入助記詞、keystore、私鑰??
1.導(dǎo)入助記詞
? ? ? ? ? ? ?即根據(jù)助記詞重新生成一個(gè)錢(qián)包。也就是前面第4點(diǎn)(生成符合bip32、bip39、bip44的錢(qián)包)。方法generateBip44Wallet()里面第1步由你導(dǎo)入的助記詞代替了,后續(xù)的代碼都不需要更改。而密碼可以是你之前的密碼也可以是新密碼,反正由seed生成私鑰的時(shí)候不需要使用到密碼,只有在生成一個(gè)keystore的時(shí)候使用密碼。
? ??? ??? ?2.導(dǎo)入keystore
? ? ? ?導(dǎo)入keystore需要輸入的密碼為生成該keystore時(shí)的密碼。將keystore字符串變成walletFile實(shí)例再通過(guò)Wallet.decrypt(password, walletFile);解密,成功則可以導(dǎo)入,否則不能導(dǎo)入。
? ??? ??? ?3.導(dǎo)入私鑰
? ??? ??? ???即根據(jù)私鑰重新生成一個(gè)錢(qián)包。密碼也可以隨意設(shè)置,跟導(dǎo)入助記詞一樣。使用的也是前面d.生成符合bip32、bip39、bip44的錢(qián)包。方法generateBip44Wallet()里面第1-6步由你導(dǎo)入的私鑰代替了,后續(xù)的代碼都不需要更改。因?yàn)樗借€在到處去的時(shí)候是轉(zhuǎn)換為了16進(jìn)制,所以需要將導(dǎo)入進(jìn)來(lái)的私鑰由16進(jìn)制轉(zhuǎn)換為byte數(shù)組? ? ? ? ?
BigInteger pk = Numeric.toBigIntNoPrefix(privateKey);
byte[] privateKeyByte = pk.toByteArray();
后續(xù)跟隨第7步。
DemoApk下載地址
github代碼地址
18.11.22更新
問(wèn)題:如果在有的手機(jī)上無(wú)法安裝請(qǐng)?jiān)赼pp目錄下的build.grade文件android節(jié)點(diǎn)下添加以下代碼
? ? //去除 bitcoinj里面使用的scrpt引用庫(kù) ,因?yàn)闆](méi)有arm的so文件會(huì)造成在arm架構(gòu)的手機(jī)上無(wú)法運(yùn)行
? ? packagingOptions {
? ? ? ? exclude 'lib/x86_64/darwin/libscrypt.dylib'
? ? ? ? exclude 'lib/x86_64/freebsd/libscrypt.so'
? ? ? ? exclude 'lib/x86_64/linux/libscrypt.so'
? ? }
?
總結(jié)
以上是生活随笔為你收集整理的以太坊钱包1-Android-创建钱包的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Libra深度解析!
- 下一篇: V神最新演讲:以太坊2.0的分片交易