这是如何开发一款以太坊 (安卓) 钱包系列第 2 篇, 如何导入账号. 有时用户可能已经有一个账号, 这篇文章接来介绍下, 如何实现导入用户已经存在的账号.
导入账号预备知识
从用户需求上来讲, 导入用户已经存在的账号是有必要的. 不过从安全性考虑, 当你之前使用的是一个非官方, 非开源的钱包产品时(尤其是小众钱包), 或者之前没有对私钥, 助记词, Keysotre 文件小心保存时.
正确的做法是提示用户:
在新的钱包重新创建一个钱包账号, 并安全备份(因为之前的可能已经不安全);
然后在老钱包里把所有的币转移到新账号.
导入账号有 3 种方式:
通过私钥导入
通过 KeyStore 导入
通过助记词导入
通过私钥导入账号
关键是用用户输入的私钥创建一个椭圆曲线秘钥对, 然后用这个秘钥对创建钱包, 代码如下:
- (代码在代码库 https://github.com/xilibi2003/Upchain-wallet 中的 App/src/pro/upchain/wallet/utils/ETHWalletUtils.java 文件中)
- public static ETHWallet loadWalletByPrivateKey(String privateKey, String pwd) {
- Credentials credentials = null;
- ECKeyPair ecKeyPair = ECKeyPair.create(Numeric.toBigInt(privateKey));
- return generateWallet(generateNewWalletName(), pwd, ecKeyPair);
- }
返回语句中的 generateWallet(), 在系列 1 - 通过助记词创建账号 已经介绍过, 通过椭圆曲线秘钥对创建钱包.
loadWalletByPrivateKey()中第 2 个参数密码 pwd, 在私钥生成账号这个过程并不需要 pwd, 它是用来加密保存私钥, 即为了生成 keystore 文件.
通过 KeyStore 文件导入账号
关于 KeyStore 文件, 不了解的可以阅读下账号 Keystore 文件导入导出.
关键步骤:
KeyStore 文本内容解析 WalletFile 实例;
使用密码 解码 WalletFile 生成椭圆曲线秘钥对创建钱包.
- /**
- * @param keystore 原 JSON 文件内容
- * @param pwd keystore 解密密码
- * @return
- */
- public static ETHWallet loadWalletByKeystore(String keystore, String pwd) {
- try {
- WalletFile walletFile = null;
- walletFile = objectMapper.readValue(keystore, WalletFile.class);
- return generateWallet(generateNewWalletName(), pwd, Wallet.decrypt(pwd, walletFile));
- } catch (IOException e) {
- } catch (CipherException e) {
- }
- return null;
- }
通过助记词导入账号
导入和上一篇中, 创建非常相似, 不同的是, 种子由用户提供的助记词生成.
使用助记词导入账号时, 还需要用户选择 (或输入) 一个推倒路径(参考 BIP44 https://learnblockchain.cn/2018/09/28/hdwallet/ ), 关键步骤是:
通过助记词创建随机数种子;
通过 种子 + 路径 派生生成私钥 创建钱包 ;
- /**
- * 通过导入助记词, 导入钱包
- *
- * @param path bip44 路径
- * @param list 助记词
- * @param pwd 密码
- * @return
- */
- public static ETHWallet importMnemonic(String path, String mnemonic, String pwd) {
- List<String> list = Arrays.asList(mnemonic.split(" "));
- if (!path.startsWith("m") && !path.startsWith("M")) {
- // 参数非法
- return null;
- }
- String[] pathArray = path.split("/");
- if (pathArray.length <= 1) {
- // 内容不对
- return null;
- }
- String passphrase = "";
- long creationTimeSeconds = System.currentTimeMillis() / 1000;
- DeterministicSeed ds = new DeterministicSeed(list, null, passphrase, creationTimeSeconds);
- return generateWalletByMnemonic(generateNewWalletName(), ds, pathArray, pwd);
- }
generateWalletByMnemonic 在上一篇中已经介绍过,
账号存储(保存到数据库)
很多同学肯定已经注意到, 不管通过什么方式构造的账号, 都会最终构造为一个 ETHWallet 钱包对象, 他的定义如下:
- @Entity
- public class ETHWallet {
- @Id(autoincrement = true)
- private Long id;
- public String address;
- private String name;
- private String password; // 经过加密后的 pwd
- private String keystorePath;
- private String mnemonic;
- private boolean isCurrent; // 是否是当前选中的钱包
- private boolean isBackup; // 是否备份过
- }
前面构造的 ETHWallet 是只存在于内容之中, 在应用程序退出之后, 这个数据将丢失, 因此我们需要把它序列化到
序列化数据库中存储起来, 在下一次进入应用的时候加载数据库还原出账号.
greenDAO
greenDAO 是一个将对象映射到 SQLite 数据库中的轻量且快速的 ORM 解决方案, 以下是一个 greenDAO 的作用示意图:
这里我们也使用了 greenDAO 来把 ETHWallet 对象映射到 SQLite 数据库, greenDAO 的用法这里只简单说明, 不详细阐述, 大家可以跟随官方提供的 和 .
对象映射保存
把 ETHWallet 映射的到数据库, 需要给类加上 @Entity 注解, 这样 greenDAO 会生成几个类: DaoMaster,DaoSession 及 ETHWalletDao 帮我们完成构建数据库表等操作.
在使用 ETHWalletDao 插入到数据库之前需要先进行一个初始化, 通常初始化放在应用程序入口中进行, 如: pro.upchain.wallet.UpChainWalletApp 的 onCreate()中执行, 初始化代码如下:
- protected void init() {
- DaoMaster.DevOpenHelper mHelper = new DaoMaster.DevOpenHelper(this, "wallet", null);
- SQLiteDatabase db = mHelper.getWritableDatabase();
- DaoSession daoSession = new DaoMaster(db).newSession();
- ETHWalletDao ethWalletDao = daoSession.getETHWalletDao();
- }
有了 greenDAO 为我们生成的辅助类, 插入到数据库就很简单了, 一行代码:
ethWalletDao.insert(ethWallet); //
ethWallet 为 ETHWallet 实例, 前面不管是新创建还是导入的账号都会构造这样一个实例.
多账号管理
考虑到用户可能会创建多个账号, 因此需要确定一个当前选定的账号, 一般情况下, 用户新创建的账号应该作为当前选中的的账号, 同时其他账号应该取消选中, 我们完善下账号存储逻辑, 如下:
- (代码在代码库 https://github.com/xilibi2003/Upchain-wallet 中的 App/src/pro/upchain/wallet/utils/WalletDaoUtils.java 文件中)
- /**
- * 插入新创建钱包
- *
- * @param ethWallet 钱
- */
- public static void insertNewWallet(ETHWallet ethWallet) {
- updateCurrent(-1); // 取消其他站账号选中状态
- ethWallet.setCurrent(true);
- ethWalletDao.insert(ethWallet);
- }
- /**
- * 更新选中钱包
- *
- * @param id 钱包 ID
- */
- public static ETHWallet updateCurrent(long id) {
- // 加载所有钱包账号
- List<ETHWallet> ethWallets = ethWalletDao.loadAll();
- ETHWallet currentWallet = null;
- for (ETHWallet ethwallet : ethWallets) {
- if (id != -1 && ethwallet.getId() == id) {
- ethwallet.setCurrent(true);
- currentWallet = ethwallet;
- } else {
- ethwallet.setCurrent(false);
- }
- ethWalletDao.update(ethwallet);
- }
- return currentWallet;
- }
打通账号创建与保存
以通过私钥导入账号进行保存为例, 把创建账号和保存账号打通, 这里我们使用响应式编程 ReactiveX,
这部分作为订阅者福利, 发表在我的小专栏 https://xiaozhuanlan.com/blockchaincore , 趁还未涨价, 赶紧订阅吧, 超值的!
学习资料
https://github.com/ReactiveX/RxAndroid/ 了解更多响应式编程
和 了解 greenDAO.
我创建了一个专门讨论钱包开发的微信群, 加微信: xlbxiong 备注: 钱包.
加入知识星球 https://t.xiaomiquan.com/RfAu7uj , 和一群优秀的区块链从业者一起学习.
深入浅出区块链 https://learnblockchain.cn/ - 系统学习区块链, 打造最好的区块链技术博客.
来源: http://www.bubuko.com/infodetail-2997637.html