以太坊调用工具类

package easyJava.controller;

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import easyJava.Configs;
import easyJava.dao.master.BaseDao;
import easyJava.entity.BaseModel;
import easyJava.entity.ResponseEntity;
import easyJava.entity.TransactionMy;
import easyJava.utils.HttpsUtils;
import easyJava.utils.MapBeanUtil;
import io.reactivex.functions.Consumer;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.java_websocket.exceptions.WebsocketNotConnectedException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.web3j.abi.FunctionEncoder;
import org.web3j.abi.TypeReference;
import org.web3j.abi.datatypes.Address;
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.admin.Admin;
import org.web3j.protocol.core.DefaultBlockParameter;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.methods.response.*;
import org.web3j.protocol.websocket.WebSocketService;
import org.web3j.tx.Transfer;
import org.web3j.utils.Convert;
import org.web3j.utils.Numeric;

import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.net.ConnectException;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.*;

@RestController
public class Web3jController {
    private static final Logger logger = LogManager.getLogger(Web3jController.class);
    @Autowired
    BaseDao baseDao;
    public static final String TRANSACTION_RECEIPT_TABLE_NAME = "accounts_coin_transaction_receipts";
    public static final String TRANSACTION_TABLE_NAME = "accounts_coin_transactions";
    public static final String ACCOUNT_TABLE_NAME = "accounts_coin_balance";
    public static final String pwd = "";
    WebSocketService ws = new WebSocketService(Configs.getEthHost(), false);
    public static final BigInteger GAS_LIMIT = BigInteger.valueOf(21000);
    public static final BigInteger ETH_WEI = BigInteger.valueOf(1000000000000000000L);

    private void initWsToEthNode() {
        try {
            ws.close();
        } catch (Exception e) {
        }
        try {
            ws = new WebSocketService(Configs.getEthHost(), false);
            //订阅交易
            // subscribeTransactions(ws);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public Web3jController() {
        try {
            ws.connect();
            subscribeTransactions(ws);
        } catch (ConnectException e) {
            e.printStackTrace();
        }
    }

    private void subscribeTransactions(WebSocketService ws) {
        Web3j web3 = Web3j.build(ws);
        //暂时屏蔽
//        web3.transactionFlowable().subscribe(new TransactionConsumer("finish") {
//        });
//        web3.pendingTransactionFlowable().subscribe(new TransactionConsumer("pending") {
//        });
    }

    public class TransactionConsumer implements Consumer<Transaction> {
        private String pending = "";

        public TransactionConsumer(String pending) {
            this.pending = pending;
        }

        private Set<String> addressSet = new HashSet<>();

        private long lastUpdate = new Date().getTime();

        @Override
        public void accept(Transaction transaction) {
            try {
                dealWithPendingTransaction(transaction);
                String tStr = JSON.toJSONString(transaction);
                TransactionMy t = JSON.parseObject(tStr, TransactionMy.class);
                t.setTime(new Date().getTime() + "");
                t.setPending(pending);
                if (hasEthAccountInOurDB(transaction.getFrom())) {
                    insertTransaction(t, TRANSACTION_TABLE_NAME);
                    return;
                }
                if (hasEthAccountInOurDB(transaction.getTo())) {
                    insertTransaction(t, TRANSACTION_TABLE_NAME);
                    return;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        private boolean hasEthAccountInOurDB(String address) {
            if (addressSet.contains(address)) {
                return true;
            } else {
                if (new Date().getTime() - lastUpdate > 10 * 1000) {
                    updateAddressSet();
                    return hasEthAccountInOurDB(address);
                }
                return false;
            }
        }

        private synchronized void updateAddressSet() {
            if (new Date().getTime() - lastUpdate < 10 * 1000) {
                return;
            }
            Map<String, Object> queryMap = new HashMap<>();
            queryMap.put("coin_name", "ETH");
            //queryMap.put("recharge_address", address);
            queryMap.put("tableName", ACCOUNT_TABLE_NAME);
            var result = baseDao.selectBaseList(queryMap, new BaseModel().setPageNo(1).setPageSize(100000).setOrderAsc("desc"));
            result.forEach(map -> {
                if (map.get("recharge_address") != null) {
                    String address = map.get("recharge_address").toString();
                    addressSet.add(address);
                }
            });
            lastUpdate = new Date().getTime();
        }

    }


    private static boolean isValidHexQuantity(String value) {
        if (value == null) {
            return false;
        } else if (value.length() < 3) {
            return false;
        } else {
            return value.startsWith("0x");
        }
    }

    private static final String DEFAULT_V = "0x111";

    private void dealWithPendingTransaction(Transaction t) {
        if (t.getNonceRaw() == null || t.getNonceRaw().length() == 0) {
            t.setNonce(DEFAULT_V);
        }
        if (t.getBlockNumberRaw() == null || t.getBlockNumberRaw().length() == 0) {
            t.setBlockNumber(DEFAULT_V);
        }
        if (t.getTransactionIndexRaw() == null || t.getTransactionIndexRaw().length() == 0) {
            t.setTransactionIndex(DEFAULT_V);
        }
        if (t.getGasPriceRaw() == null || t.getGasPriceRaw().length() == 0) {
            t.setGasPrice(DEFAULT_V);
        }
        if (t.getGasRaw() == null || t.getGasRaw().length() == 0) {
            t.setGas(DEFAULT_V);
        }
        if (t.getValueRaw() == null || t.getValueRaw().length() == 0) {
            t.setValue(DEFAULT_V);
        }
    }

    private void insertTransaction(TransactionMy transaction, String tableName) {
        Map map = JSON.parseObject(JSON.toJSON(transaction).toString(), Map.class);
        map.put("tableName", tableName);
        System.out.println(JSON.toJSON(map));
        baseDao.insertUpdateBase(map);
    }

    @GetMapping("/v1/web3j/ethHost")
    public ResponseEntity ethHost() {
        return new ResponseEntity(Configs.getEthHost());
    }

    @PostMapping("/v1/web3j/createWallet")
    public ResponseEntity createWallet(@RequestParam("uuid") String uuid) throws CipherException, InvalidAlgorithmParameterException,
            NoSuchAlgorithmException, NoSuchProviderException, IOException {
        Map map = createAccount(uuid);
        return new ResponseEntity(map);
    }

    /*************创建一个钱包文件**************/
    private static Map createAccount(String uuid) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException, CipherException, IOException {
        Map map = new HashMap();
        String walletFileName = "";//文件名
        String walletFilePath = getWalletFilePath(uuid);
        File dir = new File(walletFilePath);
        dir.setWritable(true, false);
        dir.mkdirs();
        //钱包文件保持路径,请替换位自己的某文件夹路径
        if (dir.list().length == 0) {
            walletFileName = WalletUtils.generateNewWalletFile(pwd, dir, false);
        }
        Credentials credentials = WalletUtils.loadCredentials(pwd, getWalletFilePathName(uuid));
        //钱包地址 :
        String address = credentials.getAddress();
        // 公钥16进制字符串表示:
        String publicKey = credentials.getEcKeyPair().getPublicKey().toString(16);
        //私钥16进制字符串表示:
        String privateKey = credentials.getEcKeyPair().getPrivateKey().toString(16);
        map.put("uuid", uuid);
        map.put("address", address);
        map.put("publickey", publicKey);
        map.put("privatekey", privateKey);
        map.put("walletfilename", walletFileName);
        return map;
    }

    private static String getWalletFilePath(String uuid) {
        String walletFilePath = "./eth_wallets/uid_" + uuid + "/";
        return walletFilePath;
    }

    private static String getWalletFilePathName(String uuid) {
        String walletFilePath = "./eth_wallets/uid_" + uuid + "/";
        File dir = new File(walletFilePath);
        dir.setWritable(true, false);
        String[] files = dir.list();
        if (files != null && files.length != 0) {
            return walletFilePath + files[0];
        }
        return null;
    }

    /*************创建一个钱包文件**************/
    private static Map createAccountRemote(String uuid) throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        String urlString = "https://signal.lxrtalk.com/easyJava/v1/web3j/createWallet?uuid=" + uuid;
        String response = HttpsUtils.Post(urlString, "text/json", null);
        ResponseEntity responseEntity = objectMapper.readValue(response, ResponseEntity.class);
        Map<String, Object> dataMap = responseEntity.getData();
        Map<String, Object> walletMap = (Map<String, Object>) dataMap.get("data");
        return walletMap;
    }

    /**
     * 转账金额是到账金额,手续费从转出账户余额扣除
     *
     * @param uuid
     * @param toAddress
     * @param balance
     * @return
     * @throws Exception
     */
    @PostMapping("/v1/web3j/transferWithoutFee")
    public ResponseEntity transferWithoutFee(@RequestParam("uuid") String uuid, @RequestParam("toAddress") String toAddress
            , @RequestParam("balance") double balance) throws Exception {
        Admin web3 = Admin.build(ws);  // defaults to http://localhost:8545/
        Credentials credentials = WalletUtils.loadCredentials(pwd, getWalletFilePathName(uuid));
        TransactionReceipt transactionReceipt = Transfer.sendFunds(
                        web3, credentials, toAddress,
                        BigDecimal.valueOf(balance), Convert.Unit.ETHER)
                .send();
        transactionReceipt.setLogsBloom("");
        Map map = MapBeanUtil.object2Map(transactionReceipt);
        map.remove("logs");
        map.remove("logsBloom");
        map.put("tableName", TRANSACTION_RECEIPT_TABLE_NAME);
        baseDao.insertBase(map);
        return new ResponseEntity(transactionReceipt);
    }

    /**
     * 转账金额包含手续费,因此到账会少一部分
     *
     * @param uuid
     * @param toAddress
     * @param balance
     * @return
     * @throws Exception
     */
    @PostMapping("/v1/web3j/transfer")
    public ResponseEntity transfer(@RequestParam("uuid") String uuid, @RequestParam("toAddress") String toAddress
            , @RequestParam("balance") double balance) throws Exception {
        Admin web3 = Admin.build(ws);  // defaults to http://localhost:8545/
        EthGasPrice ethGasPrice = web3.ethGasPrice().send();
        BigDecimal b = new BigDecimal(balance).multiply(new BigDecimal(ETH_WEI));
        BigDecimal balanceWithoutFee = b.subtract(new BigDecimal(ethGasPrice.getGasPrice().multiply(GAS_LIMIT)))
                .divide(new BigDecimal(ETH_WEI))
                .setScale(15, RoundingMode.DOWN);
        Credentials credentials = WalletUtils.loadCredentials(pwd, getWalletFilePathName(uuid));
        TransactionReceipt transactionReceipt = Transfer.sendFunds(
                        web3, credentials, toAddress,
                        balanceWithoutFee, Convert.Unit.ETHER)
                .send();
        transactionReceipt.setLogsBloom("");
        Map map = MapBeanUtil.object2Map(transactionReceipt);
        map.remove("logs");
        map.remove("logsBloom");
        map.put("tableName", TRANSACTION_RECEIPT_TABLE_NAME);
        baseDao.insertBase(map);
        return new ResponseEntity(transactionReceipt);
    }

    @GetMapping("/v1/web3j/transfer/history")
    public ResponseEntity history(@RequestParam("uuid") String uuid, @RequestParam("pageNo") int pageNo, @RequestParam("pageSize") int pageSize) throws IOException, CipherException {
        BaseModel baseModel = new BaseModel();
        baseModel.setPageNo(pageNo);
        baseModel.setPageSize(pageSize);
        Credentials credentials = WalletUtils.loadCredentials(pwd, getWalletFilePathName(uuid));
        Map map = new HashMap();
        map.put("to", credentials.getAddress());
        map.put("from", credentials.getAddress());
        map.put("tableName", TRANSACTION_TABLE_NAME);
        baseModel.setOrderAsc("desc");
        int count = baseDao.selectBaseCountOr(map);
        List<Map> list = baseDao.selectBaseListOr(map, baseModel);
        return new ResponseEntity(list, count, baseModel.getPageNo(), baseModel.getPageSize());
    }

    @GetMapping("/v1/web3j/balance")
    public ResponseEntity ethGetBalance(@RequestParam("address") String address) throws Exception {
        Web3j web3 = Web3j.build(ws);  // defaults to http://localhost:8545/
        try {
            EthGetBalance ret = web3.ethGetBalance(address, DefaultBlockParameter.valueOf("latest")).send();
            BigDecimal balance = new BigDecimal(ret.getBalance()).setScale(6)
                    .divide((new BigDecimal(10.000000).pow(18)).setScale(6), RoundingMode.HALF_DOWN).setScale(6);
            logger.info("ethGetBalance,address:" + address + ",balance:" + ret.getBalance());
            return new ResponseEntity(balance.toPlainString());
        } catch (WebsocketNotConnectedException e) {
            initWsToEthNode();
            logger.error("ethGetBalance", e);
        }
        return new ResponseEntity(400, "查询失败。");
    }


    @GetMapping("/v1/web3j/gasPrice")
    public ResponseEntity gasPrice() throws Exception {
        Web3j web3 = Web3j.build(ws);
        EthGasPrice ret = web3.ethGasPrice().send();
        return new ResponseEntity(ret);
    }


    @GetMapping("/v1/web3j/getTX")
    public ResponseEntity getTX(@RequestParam("transactionHash") String transactionHash) throws Exception {
        Web3j web3 = Web3j.build(ws);
        EthTransaction ret = web3.ethGetTransactionByHash(transactionHash).send();
        return new ResponseEntity(ret);
    }

    @GetMapping("/v1/web3j/estimate")
    public ResponseEntity estimate(@RequestParam("uuid") String uuid, @RequestParam("toAddress") String toAddress
            , @RequestParam("balance") double balance) {
        Web3j web3 = Web3j.build(ws);
        try {
            BigDecimal v = BigDecimal.valueOf(balance).multiply(new BigDecimal("10000000000000000"));

            EthGasPrice ethGasPrice = web3.ethGasPrice().send();
            Credentials credentials = WalletUtils.loadCredentials(pwd, getWalletFilePathName(uuid));
            EthEstimateGas gas = web3.ethEstimateGas(new org.web3j.protocol.core.methods.request.Transaction(credentials.getAddress(), BigInteger.valueOf(1),
                    ethGasPrice.getGasPrice(), GAS_LIMIT, toAddress, v.toBigInteger(), "")).send();
            return new ResponseEntity(gas.getResult());
        } catch (Exception e) {
            logger.error("estimate:", e);
        }
        return new ResponseEntity();
    }

    @PostMapping("/v1/web3j/transferETH")
    public ResponseEntity transferETH(@RequestParam("uuid") String uuid, @RequestParam("toAddress") String toAddress
            , @RequestParam("balance") double balance) throws Exception {
        Admin web3 = Admin.build(ws);
        Credentials credentials = WalletUtils.loadCredentials(pwd, getWalletFilePathName(uuid));
        TransactionReceipt transactionReceipt = Transfer.sendFunds(
                        web3, credentials, toAddress,
                        BigDecimal.valueOf(balance).multiply(new BigDecimal(10).pow(18)), Convert.Unit.WEI)
                .send();
        transactionReceipt.setLogsBloom("");
        return new ResponseEntity(transactionReceipt);
    }

    @PostMapping("/v1/web3j/transferTrash")
    public ResponseEntity transferTrash(@RequestParam("uuid") String uuid, @RequestParam("toAddress") String toAddress
            , @RequestParam("balance") double balance) throws Exception {
        String transactionHash = trashferTrashStatic(ws, uuid, toAddress, balance);
        System.out.println(transactionHash);
        //todo 日志入库
//        Map map = MapBeanUtil.object2Map(transactionReceipt);
//        map.remove("logs");
//        map.remove("logsBloom");
//        map.put("tableName", TRANSACTION_RECEIPT_TABLE_NAME);
//        baseDao.insertBase(map);
        return new ResponseEntity(transactionHash);
    }

    public static String trashferTrashStatic(WebSocketService ws, String uuid, String toAddress, double balance) {
        logger.info("-------------trashferTrashStatic start--------" + toAddress + "------" + balance);
        String transactionHash = "start";
        try {
            Admin web3j = Admin.build(ws);
            EthGasPrice ethGasPrice = web3j.ethGasPrice().send();
            logger.info("-------------trashferTrashStatic ethGasPrice--------" + ethGasPrice.getGasPrice() + "------");
            Credentials credentials = WalletUtils.loadCredentials(pwd, getWalletFilePathName(uuid));
            String fromAddress = credentials.getAddress();

            EthGetTransactionCount ethGetTransactionCount = web3j.ethGetTransactionCount(
                    fromAddress, DefaultBlockParameterName.LATEST).sendAsync().get();
            BigInteger nonce = ethGetTransactionCount.getTransactionCount();
            BigDecimal balanceDecimal = new BigDecimal(balance).multiply(new BigDecimal(10).pow(18));
            Function function = new Function(
                    "transfer",
                    Arrays.asList(new Address(toAddress), new Uint256(balanceDecimal.toBigInteger())),
                    Arrays.asList(new TypeReference<Type>() {
                    }));
            String encodedFunction = FunctionEncoder.encode(function);
            RawTransaction rawTransaction = RawTransaction.createTransaction(nonce,
                    //代币转账gas通常多一些
                    ethGasPrice.getGasPrice(),
                    GAS_LIMIT.multiply(new BigInteger("3")),
                    Configs.getTrashContractAddr(), encodedFunction);

            byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
            String hexValue = Numeric.toHexString(signedMessage);
            EthSendTransaction ethSendTransaction = web3j.ethSendRawTransaction(hexValue).send();

            transactionHash = ethSendTransaction.getTransactionHash();
            logger.info("-------------trashferTrashStatic end transactionHash:" + transactionHash + "--------");
            if (transactionHash == null) {
                logger.error("-------------trashferTrashStatic  error:" + ethSendTransaction.getError().getMessage() + "--------");
            }
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("trashferTrashStatic", e);
        }
        return transactionHash;
    }

    public static String mintTrashNFTStatic(WebSocketService ws, String uuid, String toAddress) {
        logger.info("-------------mintTrashNFTStatic start--------" + toAddress + "------");
        String transactionHash = "start";
        try {
            Admin web3j = Admin.build(ws);
            EthGasPrice ethGasPrice = web3j.ethGasPrice().send();
            logger.info("-------------mintTrashNFTStatic ethGasPrice--------" + ethGasPrice.getGasPrice() + "------");
            Credentials credentials = WalletUtils.loadCredentials(pwd, getWalletFilePathName(uuid));
            String fromAddress = credentials.getAddress();

            EthGetTransactionCount ethGetTransactionCount = web3j.ethGetTransactionCount(
                    fromAddress, DefaultBlockParameterName.LATEST).sendAsync().get();
            BigInteger nonce = ethGetTransactionCount.getTransactionCount();
            Function function = new Function(
                    "mint",
                    Arrays.asList(new Address(toAddress)),
                    Arrays.asList(new TypeReference<Type>() {
                    }));
            String encodedFunction = FunctionEncoder.encode(function);
            RawTransaction rawTransaction = RawTransaction.createTransaction(nonce,
                    //代币转账gas通常多一些
                    ethGasPrice.getGasPrice(),
                    GAS_LIMIT.multiply(new BigInteger("3")),
                    Configs.getTrashNftContractAddr(), encodedFunction);

            byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
            String hexValue = Numeric.toHexString(signedMessage);
            EthSendTransaction ethSendTransaction = web3j.ethSendRawTransaction(hexValue).send();

            transactionHash = ethSendTransaction.getTransactionHash();
            logger.info("-------------mintTrashNFTStatic end transactionHash:" + transactionHash + "--------");
            if (transactionHash == null) {
                logger.error("-------------mintTrashNFTStatic  error:" + ethSendTransaction.getError().getMessage() + "--------");
            }
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("mintTrashNFTStatic", e);
        }
        return transactionHash;
    }

    @PostMapping("/v1/web3j/systemAcc")
    public ResponseEntity systemAcc() throws Exception {
        var bigInteger=Numeric.toBigInt(Configs.getSystemPrivate());
        ECKeyPair ecKeyPair = ECKeyPair.create(bigInteger);
        //手动创建以下目录后,调用
        String walletFilePath = "./eth_wallets/uid_system/";
        String filename = WalletUtils.generateWalletFile(pwd, ecKeyPair, new File(walletFilePath), false);
        return new ResponseEntity(filename);
    }

    @PostMapping("/v1/web3j/systemNftAcc")
    public ResponseEntity systemNftAcc() throws Exception {
        var bigInteger=Numeric.toBigInt(Configs.getSystemNftPrivate());
        ECKeyPair ecKeyPair = ECKeyPair.create(bigInteger);
        //手动创建以下目录后,调用
        String walletFilePath = "./eth_wallets/uid_system_nft/";
        String filename = WalletUtils.generateWalletFile(pwd, ecKeyPair, new File(walletFilePath), false);
        return new ResponseEntity(filename);
    }

    public static void main(String[] args) throws Exception {
        var bigInteger=Numeric.toBigInt(Configs.getSystemNftPrivate());
        ECKeyPair ecKeyPair = ECKeyPair.create(bigInteger);
        System.out.println(Configs.getSystemNftPrivate());
        //手动创建以下目录后,调用
        String walletFilePath = "./";
        System.out.println(ecKeyPair.getPrivateKey());
        String filename = WalletUtils.generateWalletFile(pwd, ecKeyPair, new File(walletFilePath), false);

    }

}

  

posted @ 2022-11-03 20:07  然然1907  阅读(117)  评论(0编辑  收藏  举报