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