以太坊钱包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:
implementation 'org.web3j:core:3.3.1-android'
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里面有一个解密方法:
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数组
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-创建钱包相关推荐
- 以太坊钱包开发系列 - 创建钱包账号
想知道更多关于区块链技术知识,请百度[链客区块链技术问答社区] 链客,有问必答!! 以太坊去中心化网页钱包开发系列,将从零开始开发出一个可以实际使用的钱包,本系列文章是理论与实战相结合,一共有四篇:创 ...
- 第一行代码:以太坊(1)-创建自己的私有区块链
本文会利用以太坊客户端(geth)搭建一个私有区块链,并在这个私有区块链上挖矿,通过本文的案例,读者可以更深入理解区块链.以太坊.挖矿的理论. 通过阅读本文,您可以: 掌握搭建以太坊开发环境的方法 掌 ...
- [以太坊源代码分析] V. 从钱包到客户端
以太坊作为一种数字货币以太币的运行系统,显然它也会有类似于钱包的客户端程序,用来提供管理账户余额等功能.我们知道,存放(或者绑定,挂靠)以太币的账户,在代码中以Address类型变量存在,所以能够管理 ...
- Android以太坊钱包全部功能-基于web3j实现
文章目录 需要用到的工具 Ganache Metamask 钱包功能的具体实现 引入依赖 创建钱包 第一种创建方式 第二种创建方式 keystore导入钱包 助记词导入钱包 私钥导入钱包 Ganach ...
- 以太坊钱包开发系列3 - 展示钱包信息及发起签名交易
最新内容会更新在主站深入浅出区块链社区 原文链接:使用 ethers.js 开发以太坊 Web 钱包 3 - 展示钱包信息及发起签名交易) 以太坊去中心化网页钱包开发系列,将从零开始开发出一个可以实际 ...
- 以太坊钱包开发系列4 - 发送Token(代币)
以太坊去中心化网页钱包开发系列,将从零开始开发出一个可以实际使用的钱包,本系列文章是理论与实战相结合,一共有四篇:创建钱包账号.账号Keystore文件导入导出.展示钱包信息及发起签名交易.发送Tok ...
- 以太坊Dapp项目-网页钱包开发手册
以太坊Dapp项目-网页钱包开发手册 修订日期 姓名 邮箱 2018-10-10 brucefeng brucefeng@brucefeng.com 前言 在之前的一篇文章以太坊智能合约项目-Toke ...
- 以太坊中metamask、imtoken等钱包签名的php验证
以太坊中metamask.imtoken等钱包签名的php验证 之前开发Dapp,需要用到以太坊钱包登陆dapp,找了很久没有这方面的库,加密算法倒是有很多,直接重新写了一个库,https://git ...
- java 以太坊 智能合约_web3j教程:java使用web3j开发以太坊智能合约交易
从广义上讲,有web3j支持三种类型的以太坊交易: 1.以太币从一方交易到另一方 2.创建一个智能合约 3.与智能合约交易 为了进行这些交易,必须有以太币(以太坊区块链的代币)存在于交易发生的以太坊账 ...
最新文章
- Pandas处理时序数据(初学者必会)!
- 关于团队发展的若干想法(欢迎讨论)
- 【技术干货】Spring事务原理一探
- Struct2中三种获取表单数据的方式
- oracle truct,java向oracle 存储过程 传输数组
- 给button加href
- 友元函数的访问权限和注意事项
- Windows下Github使用方法
- 买一个二级计算机软件多少钱,计算机二级考试需要买课本吗
- 电大c 语言程序设计选择题,2020年国家开放大学电大C语言程序设计题库
- 达梦数据库错误号6002:消息校验异常
- 101个鲜为人知的超实用网站
- python微信公众号生成专属二维码--你再也不用去求人了
- 什么是 Google Play服务
- 禁止浏览器查看源代码
- 深圳基因组所刘永鑫组招聘博士后4名(3年100万+文章奖励+项目绩效)
- 人脸识别,验证,登录开发 (三)
- HDU-2036 改革春风吹满地(计算几何)
- 计算机丢失pov.dll,DolbyDAX2APOv201.dll
- C语言之volatile用法(二十一),2021最新Android面试笔试题目分享
热门文章
- js考试题 html5新特性,Web前端初级面试题总结
- oracle db file sequential read,db file sequential read等待事件
- 洛谷P1040 加分二叉树运用区间DP(动态规划)求解
- PyTorch的损失函数和优化器
- 沃流量 android,沃流量管家的身世之谜
- C++将派生类赋值给基类(向上转型)(一)
- 用 pytorch 实现 一个rnn
- Hierarchical Attention Networks for Document Classification 阅读笔记
- SparkSql读取外部数据源
- 机器学习笔记:auto encoder