以太坊钱包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:

implementation 'org.web3j:core:3.3.1-android'

bitcoinj:

implementation 'org.bitcoinj:bitcoinj-core:0.14.7'

3.生成bip39钱包(web3j直接有工具类支持)

a.直接调用WalletUtils.generateBip39Wallet()
              生成一个bip39的钱包。方法详细代码如下:

  1. /**

  2.              * 生成bip39 钱包

  3.              * @param password 生成keystore用的密码,也是用于助记词生成种子所加的盐。

  4.              * @param destinationDirectory keystore保存的目录

  5.              * @return

  6.              * @throws CipherException

  7.              * @throws IOException

  8.              */

  9.             public static Bip39Wallet generateBip39Wallet(String password, File destinationDirectory)

  10.                 throws CipherException, IOException {

  11.                    //生成标准的128位的熵,因为1byte占8位,所需要16byte。https://www.jianshu.com/p/e6a4150eb729

  12.                 byte[] initialEntropy = new byte[16];

  13.                 secureRandom.nextBytes(initialEntropy);

  14.                 //通过熵生成助记词

  15.                 String mnemonic = MnemonicUtils.generateMnemonic(initialEntropy);

  16.                 //通过助记词生成seed(种子),这里填入了password作为盐,这样会导致生成seed不够通用,

  17.                 //无法再其他的钱包(imToken等)导入。所以建议设置为null。

  18.                 byte[] seed = MnemonicUtils.generateSeed(mnemonic, password);

  19.                 //生成公私钥对(bip32,bip44规则采用的生成方式不同)

  20.                 ECKeyPair privateKey = ECKeyPair.create(sha256(seed));

  21.                 //生成WalletFile,也就是keystore的实体类,把WalletFile变为json对象就是keystore

  22.                 String walletFile = generateWalletFile(password, privateKey, destinationDirectory, false);

  23.                 return new Bip39Wallet(walletFile, mnemonic);

  24.             }

  25.             public static String generateWalletFile(

  26.                 String password, ECKeyPair ecKeyPair, File destinationDirectory, boolean useFullScrypt)

  27.                 throws CipherException, IOException {

  28.                 WalletFile walletFile;

  29.                 if (useFullScrypt) {

  30.                     //生成标准钱包(此处在android中使用一般会报oom,跟算法有关。在web3j的issus中能找到相关的内容,

  31.                     //一般android中使用的是生成轻钱包,用下面方法)

  32.                     walletFile = Wallet.createStandard(password, ecKeyPair);

  33.                 } else {

  34.                     //生成轻钱包 一般android使用此生成方式

  35.                     walletFile = Wallet.createLight(password, ecKeyPair);

  36.                 }

  37.                 //keystore文件名

  38.                 String fileName = getWalletFileName(walletFile);

  39.                 File destination = new File(destinationDirectory, fileName);

  40.                 //将keystore写入本地文件

  41.                 objectMapper.writeValue(destination, walletFile);

  42.                 return fileName;

  43.             }

2.使用web3j的注意事项。

(web3j分android版本和java版本。)在使用上述生成bip39钱包的时候有几个注意的点。
            MnemonicUtils
                在使用web3j生成助记词的时候需要使用到MnemonicUtils,如果使用库里面生成助记词的MnemonicUtils.generateMnemonic()方法,则爆出下标越界的错误:java.lang.IndexOutOfBoundsException: Index: 959

                由于此工具类读取助记词列表的代码有问题,则需要替换成java版本,或者将助记词列表放置到assets里面自己读取。我选择将MnemonicUtils整个代码copy一份将读取助记词列表的代码替换成java版本,这样的做法是在web3j的issus里面找到的解决方法。具体替换MnemonicUtils方法为:

  1. /**

  2.                  * 使用java的方式读取助记词列表

  3.                  * @return 助记词列表

  4.                  *

  5.                  * @notice 如果此处出错则将en-mnemonic-word-list.txt放入项目assets文件进行读取

  6.                  */

  7.                 private static List<String> populateWordList() {

  8.                     InputStream inputStream = Thread.currentThread().getContextClassLoader()

  9.                             .getResourceAsStream("en-mnemonic-word-list.txt");

  10.                     try {

  11.                         return readAllLines(inputStream);

  12.                     } catch (Exception e) {

  13.                         return Collections.emptyList();

  14.                     }

  15.                 }

  16.                 public static List<String> readAllLines(InputStream inputStream) throws IOException {

  17.                     BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));

  18.                     List<String> data = new ArrayList<>();

  19.                     for (String line; (line = br.readLine()) != null; ) {

  20.                         data.add(line);

  21.                     }

  22.                     return data;

  23.                 }

WalletUtils
                如果使用自己修改过的MnemonicUtils,则需要WalletUtils也替换。使用WalletUtils.generateBip39Wallet()方法时,里面用到了secureRandom,这个是SecureRandomUtils.secureRandom()生成的因为SecureRandomUtils是缺省的,所以在重写的WalletUtils无法引用,所以在使用secureRandom的地方直接使用系统的SecureRandom即可。这些代码很简单,看一眼代码一清二楚。

4.生成符合bip32、bip39、bip44的钱包

具体代码

  1. public static BaibeiWallet generateBip44Wallet(String password)

  2.             throws CipherException, IOException {

  3.             //1.通过bip39生成助记词

  4.             byte[] initialEntropy = new byte[16];

  5.             secureRandom.nextBytes(initialEntropy);

  6.             String mnemonic = MnemonicUtils.generateMnemonic(initialEntropy);

  7.             if (BuildConfig.DEBUG) {

  8.                 Log.i("TAG", "generateBip44Wallet: 助记词 = " + mnemonic);

  9.             }

  10.             //2.生成种子

  11.             byte[] seed = MnemonicUtils.generateSeed(mnemonic, null);

  12.             //3. 生成根私钥 root private key 树顶点的master key ;bip32

  13.             DeterministicKey rootPrivateKey = HDKeyDerivation.createMasterPrivateKey(seed);

  14.             if (BuildConfig.DEBUG) {

  15.                 // 根私钥进行 priB58编码,得到测试网站上显示的数据 https://iancoleman.io/bip39/

  16.                 NetworkParameters params = MainNetParams.get();

  17.                 String priv = rootPrivateKey.serializePrivB58(params);

  18.                 Log.i("TAG", "BIP32 Extended Private Key:" + priv);

  19.             }

  20.             // 4. 由根私钥生成 第一个HD 钱包

  21.             DeterministicHierarchy dh = new DeterministicHierarchy(rootPrivateKey);

  22.             // 5. 定义父路径 H则是加强 imtoken中的eth钱包进过测试发现使用的是此方式生成 bip44

  23.             List<ChildNumber> parentPath = HDUtils.parsePath("M/44H/60H/0H/0");

  24.             // 6. 由父路径,派生出第一个子私钥 "new ChildNumber(0)" 表示第一个 (m/44'/60'/0'/0/0)

  25.             DeterministicKey child = dh.deriveChild(parentPath, true, true, new ChildNumber(0));

  26.             byte[] privateKeyByte = child.getPrivKeyBytes();

  27.             //7.通过私钥生成公私钥对

  28.             ECKeyPair keyPair = ECKeyPair.create(privateKeyByte);

  29.             Log.i("TAG", "generateBip44Wallet: 钥匙对  私钥 = " + Numeric.toHexStringNoPrefix(keyPair.getPrivateKey()));

  30.             Log.i("TAG", "generateBip44Wallet: 钥匙对  公钥 = " + Numeric.toHexStringNoPrefix(keyPair.getPublicKey()));

  31.             //8.通过密码和钥匙对生成WalletFile也就是keystore的bean类

  32.             WalletFile walletFile = Wallet.createLight(password, keyPair);

  33.             //Keys.toChecksumAddress 是为了更好的显示地址降低输错概率。方法里面有eip55可以查看下

  34.             Log.i("TAG", "generateBip44Wallet: 地址 = " + Keys.toChecksumAddress(walletFile.getAddress()));

  35.             return new BaibeiWallet(walletFile, mnemonic,keyPair);

  36.            }

上面的BaibeiWallet是自己定义的一个钱包类,里面就放置了WalletFile,助记词和钥匙对。简单测试使用。

  1. public class BaibeiWallet extends Bip39Wallet {

  2.             WalletFile walletFile;

  3.             ECKeyPair mKeyPair;

  4.             public BaibeiWallet(WalletFile walletFile,String mnemonic) {

  5.                 super(null,mnemonic);

  6.                 this.walletFile = walletFile;

  7.             }

  8.             public BaibeiWallet( WalletFile walletFile,String mnemonic, ECKeyPair mKeyPair) {

  9.                 super(null, mnemonic);

  10.                 this.walletFile = walletFile;

  11.                 this.mKeyPair = mKeyPair;

  12.             }

  13.             public BaibeiWallet(String filename, String mnemonic) {

  14.                 super(filename, mnemonic);

  15.             }

  16.             public ECKeyPair getKeyPair() {

  17.                 return mKeyPair;

  18.             }

  19.             public void setKeyPair(ECKeyPair keyPair) {

  20.                 this.mKeyPair = keyPair;

  21.             }

  22.             public WalletFile getWalletFile() {

  23.                 return walletFile;

  24.             }

  25.             public void setWalletFile(WalletFile walletFile) {

  26.                 this.walletFile = walletFile;

  27.             }

  28.             public String getKeyStoreJsonString(){

  29.                 Gson gson = new Gson();

  30.                 JSONObject j = null;

  31.                 try {

  32.                     j = new JSONObject(gson.toJson(walletFile));

  33.                     //j.remove("address");

  34.                 } catch (JSONException e) {

  35.                     e.printStackTrace();

  36.                 }

  37.                 return j.toString();

  38.             }

  39.         }

e.导出助记词、keystore、私钥

1.导出助记词
              即把前面生成mnemonic展示给用户看一下。一般在导出之前都会校验一遍密码。在Wallet里面有一个解密方法:

Wallet.decrypt(password, walletFile);

第一个参数是密码,第二个参数是walletFile。这个walletFile在前面的钱包BaibeiWallet中有存储,返回一个ECKeyPair。如果方法没有抛出CipherException异常则表示解密成功,也就是可以把钱包BaibeiWallet中的mnemonic字段展示给用户。
           2.导出keystore
              同理调用Wallet.decrypt(password, walletFile);解密成功则把BaibeiWallet中的walletFile变成json字符串导出给用户。至于只是展示还是需要存储则看自己需求了,这里仅仅只是展示。
           3.导出私钥
               同理调用Wallet.decrypt(password, walletFile);解密成功则把返回的ECKeyPair中的私钥转换为16进制导出

 String privateKey = Numeric.toHexStringNoPrefix(ecKeyPair.getPrivateKey());

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数组

  1. BigInteger pk = Numeric.toBigIntNoPrefix(privateKey);

  2. 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-创建钱包相关推荐

  1. 以太坊钱包开发系列 - 创建钱包账号

    想知道更多关于区块链技术知识,请百度[链客区块链技术问答社区] 链客,有问必答!! 以太坊去中心化网页钱包开发系列,将从零开始开发出一个可以实际使用的钱包,本系列文章是理论与实战相结合,一共有四篇:创 ...

  2. 第一行代码:以太坊(1)-创建自己的私有区块链

    本文会利用以太坊客户端(geth)搭建一个私有区块链,并在这个私有区块链上挖矿,通过本文的案例,读者可以更深入理解区块链.以太坊.挖矿的理论. 通过阅读本文,您可以: 掌握搭建以太坊开发环境的方法 掌 ...

  3. [以太坊源代码分析] V. 从钱包到客户端

    以太坊作为一种数字货币以太币的运行系统,显然它也会有类似于钱包的客户端程序,用来提供管理账户余额等功能.我们知道,存放(或者绑定,挂靠)以太币的账户,在代码中以Address类型变量存在,所以能够管理 ...

  4. Android以太坊钱包全部功能-基于web3j实现

    文章目录 需要用到的工具 Ganache Metamask 钱包功能的具体实现 引入依赖 创建钱包 第一种创建方式 第二种创建方式 keystore导入钱包 助记词导入钱包 私钥导入钱包 Ganach ...

  5. 以太坊钱包开发系列3 - 展示钱包信息及发起签名交易

    最新内容会更新在主站深入浅出区块链社区 原文链接:使用 ethers.js 开发以太坊 Web 钱包 3 - 展示钱包信息及发起签名交易) 以太坊去中心化网页钱包开发系列,将从零开始开发出一个可以实际 ...

  6. 以太坊钱包开发系列4 - 发送Token(代币)

    以太坊去中心化网页钱包开发系列,将从零开始开发出一个可以实际使用的钱包,本系列文章是理论与实战相结合,一共有四篇:创建钱包账号.账号Keystore文件导入导出.展示钱包信息及发起签名交易.发送Tok ...

  7. 以太坊Dapp项目-网页钱包开发手册

    以太坊Dapp项目-网页钱包开发手册 修订日期 姓名 邮箱 2018-10-10 brucefeng brucefeng@brucefeng.com 前言 在之前的一篇文章以太坊智能合约项目-Toke ...

  8. 以太坊中metamask、imtoken等钱包签名的php验证

    以太坊中metamask.imtoken等钱包签名的php验证 之前开发Dapp,需要用到以太坊钱包登陆dapp,找了很久没有这方面的库,加密算法倒是有很多,直接重新写了一个库,https://git ...

  9. java 以太坊 智能合约_web3j教程:java使用web3j开发以太坊智能合约交易

    从广义上讲,有web3j支持三种类型的以太坊交易: 1.以太币从一方交易到另一方 2.创建一个智能合约 3.与智能合约交易 为了进行这些交易,必须有以太币(以太坊区块链的代币)存在于交易发生的以太坊账 ...

最新文章

  1. Pandas处理时序数据(初学者必会)!
  2. 关于团队发展的若干想法(欢迎讨论)
  3. 【技术干货】Spring事务原理一探
  4. Struct2中三种获取表单数据的方式
  5. oracle truct,java向oracle 存储过程 传输数组
  6. 给button加href
  7. 友元函数的访问权限和注意事项
  8. Windows下Github使用方法
  9. 买一个二级计算机软件多少钱,计算机二级考试需要买课本吗
  10. 电大c 语言程序设计选择题,2020年国家开放大学电大C语言程序设计题库
  11. 达梦数据库错误号6002:消息校验异常
  12. 101个鲜为人知的超实用网站
  13. python微信公众号生成专属二维码--你再也不用去求人了
  14. 什么是 Google Play服务
  15. 禁止浏览器查看源代码
  16. 深圳基因组所刘永鑫组招聘博士后4名(3年100万+文章奖励+项目绩效)
  17. 人脸识别,验证,登录开发 (三)
  18. HDU-2036 改革春风吹满地(计算几何)
  19. 计算机丢失pov.dll,DolbyDAX2APOv201.dll
  20. C语言之volatile用法(二十一),2021最新Android面试笔试题目分享

热门文章

  1. js考试题 html5新特性,Web前端初级面试题总结
  2. oracle db file sequential read,db file sequential read等待事件
  3. 洛谷P1040 加分二叉树运用区间DP(动态规划)求解
  4. PyTorch的损失函数和优化器
  5. 沃流量 android,沃流量管家的身世之谜
  6. C++将派生类赋值给基类(向上转型)(一)
  7. 用 pytorch 实现 一个rnn
  8. Hierarchical Attention Networks for Document Classification 阅读笔记
  9. SparkSql读取外部数据源
  10. 机器学习笔记:auto encoder