使用web3j构建以太坊钱包

建立一个以太坊钱包有多种方式,通常状况下能够经过geth、EtherumWallet等客户端。对于前端,可使用插件MetaMask进行建立。这几种方式技术实现虽然不一样,但底层原理是一致的。本文主要介绍如何经过web3j架构建立一个以太坊的冷钱包,从而实现将这一过程部署在服务端或者android端。html

文中涉及到的技术栈有:前端

Web3j :轻量级java库,用于链接以太坊客户端或节点java

Infura :以太坊基础设施,用于访问以太坊主网络或测试网络android

Java:编程语言git

1.Web3j的安装

不管是java工程仍是android工程,web3j都提供了maven和grade 两种依赖方式:github

  1. java工程
  • manen依赖
<dependency>
  <groupId>org.web3j</groupId>
  <artifactId>core</artifactId>
  <version>3.3.1</version>
</dependency>
复制代码
  • gradle依赖
compile ('org.web3j:core:3.3.1')
复制代码
  1. android工程
  • maven依赖
<dependency>
  <groupId>org.web3j</groupId>
  <artifactId>core</artifactId>
  <version>3.3.1-android</version>
</dependency>
复制代码
  • gradle依赖
compile ('org.web3j:core:3.3.1-android')
复制代码

值得注意的是,目前的web3j对于高版本JDK存在不兼容的问题,若是出现以下相似的问题,直接更换JDK为version 8便可。web

Could not determine Java version using executable /Library/Java/JavaVirtualMachines/jdk-10.jdk/Contents/Home/bin/java.算法

2.关于Infura

以太坊的客户端实现有多种,但不少都须要在本地同步全部的节点数据而占用大量硬盘存储空间,而且须要消耗同步的时间。Infura就是提供一种中心化的服务,经过web3.js或者web3j使前端或服务端能便捷的访问以太坊全部节点。能够理解为一种以太坊客户端的云端版本。使用过程须要注册,一个专属的访问token。本文中使用的客户端都是Infura提供的Rinkeby测试网络。编程

3.新建钱包文件keyfile

在以太坊中,钱包(wallet)和帐户(account)是两个不一样的概念。帐户是以太坊的核心,由一对秘钥组成-公钥和私钥。帐户能够分为两种,外部帐户和合约帐户。而钱包是指保存 地址、公钥、私钥的文件或其余机构,每一个钱包文件至少包含一个帐户。建立钱包的同时也是建立一个以太坊帐户的过程不一样的客户端建立钱包的方式不一致但原理相同,有关钱包是具体是如何生成的能够查看另外这篇文章。json

  1. 新建一个java工程,初始化gradle或者maven
  2. 依赖web3j
  3. 新建Application.java文件,设置程序入口main函数
  4. 调用钱包工具类生成钱包文件
/*************建立一个钱包文件**************/
private void creatAccount() throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException, CipherException, IOException {
    String walletFileName0="";//文件名
    String walletFilePath0="/Users/yepeng/MyGitHub/z_wallet_temp";
    //钱包文件保持路径,请替换位本身的某文件夹路径

    walletFileName0 = WalletUtils.generateNewWalletFile("123456", new File(walletFilePath0), false);
    //WalletUtils.generateFullNewWalletFile("password1",new File(walleFilePath1));
    //WalletUtils.generateLightNewWalletFile("password2",new File(walleFilePath2));
    log.info("walletName: "+walletFileName0);
}
复制代码

钱包构建的过程当中须要输入的三个参数,分别设置钱包的密码、保存路径、以及是否轻量级钱包。

执行建立函数后,会自动在指定路径生成一个json 文件,即钱包keyfiles。

钱包文件结构:

  • cipher:加密算法,AES算法,用于加密以太坊私钥
  • cipherparams:cipher算法须要的参数,参数iv,是aes-128-ctr加密算法须要的初始化向量
  • ciphertext:加密后的密文,aes-128-ctr函数的加密输入密文;
  • kdf:秘钥生成函数,用于使用密码加密keystore文件
  • kdfparams:kdf算法所须要的参数
  • mac:验证密码的编码

生成钱包的逆向过程 为加载钱包。

4.加载钱包文件

加载钱包的过程须要提供钱包文件和密码

/********加载钱包文件**********/
private void loadWallet() throws IOException, CipherException {
    String walleFilePath="/Users/yepeng/MyGitHub/z_wallet_temp/UTC--2018-04-10T02-51-24.815000000Z--12571f46ec3f81f7ebe79112be5883194d683787.json";
    String passWord="123456";
    credentials = WalletUtils.loadCredentials(passWord, walleFilePath);
    String address = credentials.getAddress();
    BigInteger publicKey = credentials.getEcKeyPair().getPublicKey();
    BigInteger privateKey = credentials.getEcKeyPair().getPrivateKey();

    log.info("address="+address);
    log.info("public key="+publicKey);
    log.info("private key="+privateKey);

}
复制代码

函数运行的结果:

经过工具类 WalletUtols的函数 loadCredentials(),会返回一个对象Credentials,这个对象即包含了钱包文件的全部信息,包括地址、秘钥对。

至此,钱包的建立和加载已经完成,但这一过程所有发生在本地,并未同步到以太坊区块链。查询地址余额前,须要链接以太坊结点。

5.构建Web3j实体,链接以太坊结点

web3j是链接java端与以太坊的桥梁,广播交易,查询帐户都须要经过web3j实体。web3j支持经过http进行构建,并且兼容了infura。在本文中,使用的是infura的测试网络Rinkeby。

/*******链接以太坊客户端**************/
private void conectETHclient() throws IOException {
    //链接方式1:使用infura 提供的客户端
    web3j = Web3j.build(new HttpService("https://rinkeby.infura.io/zmd7VgRt9go0x6qlJ2Mk"));// TODO: 2018/4/10 token更改成本身的
    //链接方式2:使用本地客户端
    //web3j = Web3j.build(new HttpService("127.0.0.1:7545"));
    //测试是否链接成功
    String web3ClientVersion = web3j.web3ClientVersion().send().getWeb3ClientVersion();
    log.info("version=" + web3ClientVersion);
}
复制代码

web3j实体构建完成后,能够打印出版本号以测试是否链接成功。若是成功,就能够作其余的事情了。值得注意的是,web3j采用的是RxJava的设计,因此许多函数的返回值是 Request,这个对象有两种执行方式,异步和同步,即send()和sendAsyn()。

6.查询帐户余额

查询帐户的余额的方式:

/***********查询指定地址的余额***********/
private void getBlanceOf() throws IOException {
    if (web3j == null) return;
    String address = "0x41F1dcbC0794BAD5e94c6881E7c04e4F98908a87";//等待查询余额的地址
    //第二个参数:区块的参数,建议选最新区块
    EthGetBalance balance = web3j.ethGetBalance(address, DefaultBlockParameter.valueOf("latest")).send();
    //格式转化 wei-ether
    String blanceETH = Convert.fromWei(balance.getBalance().toString(), Convert.Unit.ETHER).toPlainString().concat(" ether");
    log.info(blanceETH);
}
复制代码

其中核心方法 web3j.ethGetBalance(address, defaultBlockParameter) 中的第二个参数比较特殊,指默认的区块参数。当请求余额的方法做用与以太坊的区块网络时,这个参数决定了查询区块的高度。

  • HEX String - 一个整数块号
  • String "earliest" 为最先/起源块
  • String "latest" - 为最新的采矿块
  • String "pending" - 待处理状态/交易

通常状况下,选择“latest”便可。

以太坊中,若是没有特殊标示,数字的单位都是小数点后18位,所以查询帐户余额有必要将wei转化成ether

6.使用钱包进行转帐

做为一个钱包,除了保存帐户资产外,最重要的就是转帐或交易了,利用web3j能够便捷的实现eth的转移。

/    /****************交易*****************/
    private void transto() throws Exception {
        if (web3j == null) return;
        if (credentials == null) return;
        //开始发送0.01 =eth到指定地址
        String address_to = "0x41F1dcbC0794BAD5e94c6881E7c04e4F98908a87";
        TransactionReceipt send = Transfer.sendFunds(web3j, credentials, address_to, BigDecimal.ONE, Convert.Unit.FINNEY).send();

        log.info("Transaction complete:");
        log.info("trans hash=" + send.getTransactionHash());
        log.info("from :" + send.getFrom());
        log.info("to:" + send.getTo());
        log.info("gas used=" + send.getGasUsed());
        log.info("status: " + send.getStatus());
    }
复制代码

核心方法须要提供4个参数:

  • web3j实体
  • Credentials 源帐户
  • address 转出地址
  • value 数量
  • uint 单位

等待片刻后,会返回转帐结果

能够看到交易hash、转入转出地址、gas消耗等信息。同时能够在etherscan-rinkeby上进行查看本次交易详情

7.总结

上面的代码已经完成了一个以太坊钱包所需的全部基本功能,包括建立、加载、转帐、查询。本文中采用的网络是infura提供的Rinkeby测试网络,建立的钱包地址为 0x12571F46Ec3f81F7EbE79112Be5883194d683787

在具体的业务场景中,只要将测试网络更换为以太坊主网络便可。

源码地址 github.com/initsysctrl…

相关文章
相关标签/搜索