eth錢(qián)包開(kāi)發(fā)--java(附帶eth離線交易工具類(lèi))

本篇主要說(shuō)明如何離線生成ETH地址和進(jìn)行離線交易

通過(guò)助記詞離線生成錢(qián)包地址

關(guān)于助記詞和HD錢(qián)包原理的原理可以參考以下鏈接的內(nèi)容
http://www.itdecent.cn/p/e6a4150eb729

大致過(guò)程如下圖:


derivation.png

由于在web3j庫(kù)中沒(méi)有加入助記詞派生地址的解決方法,所以我們離線生成地址時(shí)需要引入bitcoinj庫(kù)提供方法。

以下為需要的錢(qián)包相關(guān)依賴(lài):

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

進(jìn)行離線交易

原理:將交易的原始信息包括nonce(交易次數(shù))、gasPrice、gasLimit、from、to、amount等構(gòu)造完成后進(jìn)行編碼,簽名,最后廣播至區(qū)塊鏈上。

以下是eth方法工具類(lèi):

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"));

    /**
     * 通過(guò)助記詞和id生成對(duì)應(yīng)的子賬戶
     * @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();
    }


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


    /**
     * 發(fā)送eth離線交易
     * @param from eth持有地址
     * @param to 發(fā)送目標(biāo)地址
     * @param amount 金額(單位:eth)
     * @param credentials 秘鑰對(duì)象
     * @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();
    }

    /**
     * 發(fā)送代幣離線交易
     * @param from 代幣持有地址
     * @param to 代幣目標(biāo)地址
     * @param amount 金額(單位:代幣最小單位)
     * @param coinAddress 代幣合約地址
     * @param credentials 秘鑰對(duì)象
     * @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();
    }

    /**
     * 發(fā)送賬戶內(nèi)所有eth
     * @param from 持有地址
     * @param to 目標(biāo)地址
     * @param credentials 秘鑰對(duì)象
     * @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();
    }

    /**
     * 發(fā)送賬戶內(nèi)所有某代幣
     * @param from 代幣擁有地址
     * @param to 代幣目標(biāo)地址
     * @param coinAddress 代幣合約地址
     * @param gasLimit gas值
     * @param gasPrice gas price
     * @param credentials 秘鑰對(duì)象
     * @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 發(fā)送者
     * @param to 發(fā)送目標(biāo)地址
     * @param coinAddress 代幣地址
     * @param value 發(fā)送金額(單位:代幣最小單位)
     * @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();
    }

}


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容