eth钱包开发--java(附带eth离线交易工具类)

2019-01-10  本文已影响0人  zachary_lee726

本篇主要说明如何离线生成ETH地址和进行离线交易

通过助记词离线生成钱包地址

关于助记词和HD钱包原理的原理可以参考以下链接的内容
https://www.jianshu.com/p/e6a4150eb729

大致过程如下图:


derivation.png

由于在web3j库中没有加入助记词派生地址的解决方法,所以我们离线生成地址时需要引入bitcoinj库提供方法。

以下为需要的钱包相关依赖:

<dependency>
        <groupId>org.web3j</groupId>
        <artifactId>core</artifactId>
        <version>4.0.3</version>
</dependency>

<dependency>
        <groupId>org.bitcoinj</groupId>
        <artifactId>bitcoinj-core</artifactId>
        <version>0.14.7</version>
</dependency>

进行离线交易

原理:将交易的原始信息包括nonce(交易次数)、gasPrice、gasLimit、from、to、amount等构造完成后进行编码,签名,最后广播至区块链上。

以下是eth方法工具类:

import com.google.common.collect.ImmutableList;
import org.bitcoinj.crypto.ChildNumber;
import org.bitcoinj.crypto.DeterministicHierarchy;
import org.bitcoinj.crypto.DeterministicKey;
import org.bitcoinj.crypto.HDKeyDerivation;
import org.web3j.abi.FunctionEncoder;
import org.web3j.abi.TypeReference;
import org.web3j.abi.datatypes.Function;
import org.web3j.abi.datatypes.Type;
import org.web3j.abi.datatypes.generated.Uint256;
import org.web3j.crypto.*;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.methods.request.Transaction;
import org.web3j.protocol.http.HttpService;
import org.web3j.utils.Convert;
import org.web3j.utils.Numeric;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collections;
import java.util.concurrent.ExecutionException;

public class EthereumManager {

    private final static ImmutableList<ChildNumber> BIP44_ETH_ACCOUNT_ZERO_PATH =
            ImmutableList.of(new ChildNumber(44, true), new ChildNumber(60, true),
                    ChildNumber.ZERO_HARDENED, ChildNumber.ZERO);

    private final static Web3j web3j = Web3j.build(new HttpService("localhost:8545"));

    /**
     * 通过助记词和id生成对应的子账户
     * @param mnemonic 助记词
     * @param id 派生子id
     * @return 子账户key
     */
    private static DeterministicKey generateKeyFromMnemonicAndUid(String mnemonic, int id) {
        byte[] seed = MnemonicUtils.generateSeed(mnemonic, "");

        DeterministicKey rootKey = HDKeyDerivation.createMasterPrivateKey(seed);
        DeterministicHierarchy hierarchy = new DeterministicHierarchy(rootKey);

        return hierarchy.deriveChild(BIP44_ETH_ACCOUNT_ZERO_PATH, false, true, new ChildNumber(id, false));
    }

    /**
     * 生成地址
     * @param id 用户id
     * @return 地址
     * @throws CipherException
     */
    public static String getEthAddress(String mnemonic, int id) {
        DeterministicKey deterministicKey = generateKeyFromMnemonicAndUid(mnemonic, id);
        ECKeyPair ecKeyPair = ECKeyPair.create(deterministicKey.getPrivKey());
        return Keys.getAddress(ecKeyPair);
    }

    /**
     * 生成私钥
     * @param id 用户id
     * @return 私钥
     */
    public static BigInteger getPrivateKey(String mnemonic, int id) {
        return generateKeyFromMnemonicAndUid(mnemonic, id).getPrivKey();
    }


    /**
     * 通过private key生成credentials
     */
    public static Credentials generateCredentials(String privateKey) {
        return Credentials.create(privateKey);
    }


    /**
     * 发送eth离线交易
     * @param from eth持有地址
     * @param to 发送目标地址
     * @param amount 金额(单位:eth)
     * @param credentials 秘钥对象
     * @return 交易hash
     * @throws IOException
     * @throws ExecutionException
     * @throws InterruptedException
     */
    public static String sendEthRawTransaction(String from, String to, BigDecimal amount, Credentials credentials) throws IOException, ExecutionException, InterruptedException {

        BigInteger nonce = web3j.ethGetTransactionCount(from, DefaultBlockParameterName.PENDING).send().getTransactionCount();
        BigInteger gasPrice = web3j.ethGasPrice().send().getGasPrice();
        BigInteger gasLimit = BigInteger.valueOf(21000L);

        BigInteger amountWei = Convert.toWei(amount, Convert.Unit.ETHER).toBigInteger();

        RawTransaction rawTransaction = RawTransaction.createTransaction(nonce, gasPrice, gasLimit, to, amountWei, "");

        byte[] signMessage = TransactionEncoder.signMessage(rawTransaction, credentials);

        return web3j.ethSendRawTransaction(Numeric.toHexString(signMessage)).sendAsync().get().getTransactionHash();
    }

    /**
     * 发送代币离线交易
     * @param from 代币持有地址
     * @param to 代币目标地址
     * @param amount 金额(单位:代币最小单位)
     * @param coinAddress 代币合约地址
     * @param credentials 秘钥对象
     * @return 交易hash
     * @throws IOException
     * @throws ExecutionException
     * @throws InterruptedException
     */
    public static String sendContractTransaction(String from, String to, BigInteger gasLimit, BigInteger amount, String coinAddress, Credentials credentials) throws IOException, ExecutionException, InterruptedException {
        BigInteger nonce = web3j.ethGetTransactionCount(from, DefaultBlockParameterName.PENDING).send().getTransactionCount();
        BigInteger gasPrice = web3j.ethGasPrice().send().getGasPrice();

        Function function = new Function(
                "transfer",
                Arrays.<Type>asList(new org.web3j.abi.datatypes.Address(to),
                        new org.web3j.abi.datatypes.generated.Uint256(amount)),
                Collections.<TypeReference<?>>emptyList());
        String data = FunctionEncoder.encode(function);

        RawTransaction rawTransaction = RawTransaction.createTransaction(nonce, gasPrice, gasLimit, coinAddress, data);
        byte[] signMessage = TransactionEncoder.signMessage(rawTransaction, credentials);

        return web3j.ethSendRawTransaction(Numeric.toHexString(signMessage)).sendAsync().get().getTransactionHash();
    }

    /**
     * 发送账户内所有eth
     * @param from 持有地址
     * @param to 目标地址
     * @param credentials 秘钥对象
     * @return 交易hash
     * @throws IOException
     */
    public static String sendAllEth(String from, String to, Credentials credentials) throws IOException {

        BigInteger nonce = web3j.ethGetTransactionCount(from, DefaultBlockParameterName.PENDING).send().getTransactionCount();
        BigInteger gasPrice = web3j.ethGasPrice().send().getGasPrice();
        BigInteger gasLimit = BigInteger.valueOf(21000L);
        BigInteger balance = web3j.ethGetBalance(from, DefaultBlockParameterName.PENDING).send().getBalance();

        if (balance.compareTo(gasPrice.multiply(gasLimit)) <= 0) {
            return null;
        }

        BigInteger amount = balance.subtract(gasPrice.multiply(gasLimit));

        RawTransaction transaction = RawTransaction.createTransaction(nonce, gasPrice, gasLimit, to, amount, "");
        byte[] signMessage = TransactionEncoder.signMessage(transaction, credentials);
        return web3j.ethSendRawTransaction(Numeric.toHexString(signMessage)).send().getTransactionHash();
    }

    /**
     * 发送账户内所有某代币
     * @param from 代币拥有地址
     * @param to 代币目标地址
     * @param coinAddress 代币合约地址
     * @param gasLimit gas值
     * @param gasPrice gas price
     * @param credentials 秘钥对象
     * @return 交易hash
     * @throws IOException
     */
    public static String sendAllCoin(String from, String to, String coinAddress, BigInteger gasLimit, BigInteger gasPrice, Credentials credentials) throws IOException {
        BigInteger nonce = web3j.ethGetTransactionCount(from, DefaultBlockParameterName.PENDING).send().getTransactionCount();

        BigInteger value = getBalanceOfCoin(from, coinAddress);
        System.out.println(value);

        Function transfer = new Function(
                "transfer",
                Arrays.<Type>asList(new org.web3j.abi.datatypes.Address(to),
                        new org.web3j.abi.datatypes.generated.Uint256(value)),
                Collections.<TypeReference<?>>emptyList());
        String data = FunctionEncoder.encode(transfer);

        RawTransaction rawTransaction = RawTransaction.createTransaction(nonce, gasPrice, gasLimit, coinAddress, data);

        byte[] signMessage = TransactionEncoder.signMessage(rawTransaction, credentials);

        return web3j.ethSendRawTransaction(Numeric.toHexString(signMessage)).send().getTransactionHash();

    }

    /**
     * 获取账户代币余额
     * @param account 账户地址
     * @param coinAddress 代币地址
     * @return 代币余额 (单位:代币最小单位)
     * @throws IOException
     */
    public static BigInteger getBalanceOfCoin(String account, String coinAddress) throws IOException {
        Function balanceOf = new Function("balanceOf",
                Arrays.<Type>asList(new org.web3j.abi.datatypes.Address(account)),
                Arrays.<TypeReference<?>>asList(new TypeReference<Uint256>() {
                }));

        if (coinAddress == null) {
            return null;
        }
        String value = web3j.ethCall(Transaction.createEthCallTransaction(account, coinAddress, FunctionEncoder.encode(balanceOf)), DefaultBlockParameterName.PENDING).send().getValue();
        return new BigInteger(value.substring(2), 16);
    }

    /**
     * 获取合约交易估算gas值
     * @param from 发送者
     * @param to 发送目标地址
     * @param coinAddress 代币地址
     * @param value 发送金额(单位:代币最小单位)
     * @return 估算的gas limit
     * @throws IOException
     */
    public static BigInteger getTransactionGasLimit(String from, String to, String coinAddress, BigInteger value) throws IOException {
        Function transfer = new Function(
                "transfer",
                Arrays.<Type>asList(new org.web3j.abi.datatypes.Address(to),
                        new org.web3j.abi.datatypes.generated.Uint256(value)),
                Collections.<TypeReference<?>>emptyList());
        String data = FunctionEncoder.encode(transfer);
        return web3j.ethEstimateGas(new Transaction(from, null, null, null, coinAddress, BigInteger.ZERO, data)).send().getAmountUsed();
    }

}


上一篇下一篇

猜你喜欢

热点阅读