BTC离线签名,矿工费计算,生成BTC密钥

本篇文章,我们使用开源项目bitcoinj解决BTC离线签名,矿工费计算,生成BTC秘钥等问题。

1.首先添加如下maven标签

         
            org.bitcoinj
            bitcoinj-core
            0.14.7
        
        
        
            org.apache.commons
            commons-lang3
            3.8
        

2.上代码

    //是否主網(default:true)
    public Boolean isMainNet = true;

    public void setIsMainNet(Boolean isMainNet) {
        this.isMainNet = isMainNet;
    }

    public static final String privateKey_K = "privateKey";
    public static final String publicKey_K = "privateKey";
    public static final String address_K = "address";
    public static final String mnemonics_K = "mnemonics";
    /**
     * btc交易签名
     * @param fromAddress
     * @param toAddress
     * @param privateKey
     * @param amount
     * @param fee
     * @param utxos
     * @return
     * @throws Exception
     */
    public String sign(String fromAddress, String toAddress, String privateKey, long amount, long fee, List utxos) throws Exception {
        Res res = new Res();
        NetworkParameters networkParameters = isMainNet ? MainNetParams.get() : TestNet3Params.get();
        Transaction transaction = new Transaction(networkParameters);

        String changeAddress = fromAddress;//找零地址
        Long changeAmount = 0L;
        Long utxoAmount = 0L;
        List needUtxos = new ArrayList();
        //获取未消费列表
        if (utxos == null || utxos.size() == 0) {
            throw new Exception("未消费列表为空");
        }
        //遍历未花费列表,组装合适的item
        for (UTXO utxo : utxos) {
            if (utxoAmount >= (amount + fee)) {
                break;
            } else {
                needUtxos.add(utxo);
                utxoAmount += utxo.getValue().value;
            }
        }
        transaction.addOutput(Coin.valueOf(amount), Address.fromBase58(networkParameters, toAddress));
        //消费列表总金额 - 已经转账的金额 - 手续费 就等于需要返回给自己的金额了
        changeAmount = utxoAmount - (amount + fee);
        //余额判断
        if (changeAmount < 0) {
            throw new Exception("utxo余额不足");
        }
        //输出-转给自己(找零)
        if (changeAmount > 0) {
            transaction.addOutput(Coin.valueOf(changeAmount), Address.fromBase58(networkParameters, changeAddress));
        }
        //输入未消费列表项
        DumpedPrivateKey dumpedPrivateKey = DumpedPrivateKey.fromBase58(networkParameters, privateKey);
        ECKey ecKey = dumpedPrivateKey.getKey();
        for (UTXO utxo : needUtxos) {
            TransactionOutPoint outPoint = new TransactionOutPoint(networkParameters, utxo.getIndex(), utxo.getHash());
            transaction.addSignedInput(outPoint, utxo.getScript(), ecKey, Transaction.SigHash.ALL, true);
        }
        byte[] bytes = transaction.bitcoinSerialize();
        String hash = Hex.toHexString(transaction.bitcoinSerialize());
        logger.info("fee:{},utxoAmount:{},changeAmount{}", fee, utxoAmount, changeAmount);
        return hash;
    }

    /**
     * 获取矿工费用
     * @param amount
     * @param utxos
     * @return
     */
    public Long getFee(long amount, List utxos) {
        Long feeRate = getFeeRate();//获取费率
        Long utxoAmount = 0L;
        Long fee = 0L;
        Long utxoSize = 0L;
        for (UTXO us : utxos) {
            utxoSize++;
            if (utxoAmount >= (amount + fee)) {
                break;
            } else {
                utxoAmount += us.getValue().value;
                fee = (utxoSize * 148 * 34 * 3 + 10) * feeRate;
            }
        }
        return fee;
    }
 
    /**
     * 创建钱包地址
     *
     * @return
     */
    public Map createWalletToJson() {
        NetworkParameters networkParameters = isMainNet ? MainNetParams.get() : TestNet3Params.get();
        DeterministicSeed seed = new DeterministicSeed(new SecureRandom(), 128, "", Utils.currentTimeSeconds());
        Wallet wallet;
        String mnemonics = "";
        String privateKey = "";
        String publicKey = "";
        String address = "";
        String pwd = "";
        try {
            wallet = Wallet.fromSeed(networkParameters, seed);
            //私钥
            privateKey = wallet.currentReceiveKey().getPrivateKeyAsWiF(networkParameters);
            //助记词
            mnemonics = wallet.getKeyChainSeed().getMnemonicCode().toString();
            publicKey = Hex.toHexString(ECKey.publicKeyFromPrivate(wallet.currentReceiveKey().getPrivKey(), true));
            //地址
            address = wallet.currentReceiveAddress().toBase58();
        } catch (Exception e) {
            logger.error("【比特币钱包创建】失败,原因", e);
            return null;
        }
        Map resultMap = new LinkedHashMap();
        resultMap.put(mnemonics_K, mnemonics);
        resultMap.put(privateKey_K, privateKey);
        resultMap.put(publicKey_K, publicKey);
        resultMap.put(address_K, address);
        return resultMap;
    }


    /***
     * 获取未消费列表
     * @param address :地址
     * @return
     */
    public List getUnspent(String address) {
        List utxos = Lists.newArrayList();
        String host = this.isMainNet ? "blockchain.info" : "testnet.blockchain.info";
        String url = "https://" + host + "/zh-cn/unspent?active=" + address;
        try {
            String httpGet = HttpUtil.sendGet(url, null);//TODO;联网
            if (StringUtils.equals("No free outputs to spend", httpGet)) {
                return utxos;
            }
            JSONObject jsonObject = JSON.parseObject(httpGet);
            JSONArray unspentOutputs = jsonObject.getJSONArray("unspent_outputs");
            List outputs = JSONObject.parseArray(unspentOutputs.toJSONString(), Map.class);
            if (outputs == null || outputs.size() == 0) {
                System.out.println("交易异常,余额不足");
            }
            for (int i = 0; i < outputs.size(); i++) {
                Map outputsMap = outputs.get(i);
                String tx_hash = outputsMap.get("tx_hash").toString();
                String tx_hash_big_endian = outputsMap.get("tx_hash_big_endian").toString();
                String tx_index = outputsMap.get("tx_index").toString();
                String tx_output_n = outputsMap.get("tx_output_n").toString();
                String script = outputsMap.get("script").toString();
                String value = outputsMap.get("value").toString();
                String value_hex = outputsMap.get("value_hex").toString();
                String confirmations = outputsMap.get("confirmations").toString();
                UTXO utxo = new UTXO(Sha256Hash.wrap(tx_hash_big_endian), Long.valueOf(tx_output_n), Coin.valueOf(Long.valueOf(value)),
                        0, false, new Script(Hex.decode(script)));
                utxos.add(utxo);
            }
            return utxos;
        } catch (Exception e) {
            logger.error("【BTC获取未消费列表】失败,", e);
            return null;
        }

    }

    /**
     * 获取btc费率
     *
     * @return
     */
    public Long getFeeRate() {
        try {
            String httpGet1 = HttpUtil.sendGet("https://bitcoinfees.earn.com/api/v1/fees/recommended", null);
            Map map = JSON.parseObject(httpGet1, Map.class);
            Long fastestFee = Long.valueOf(map.get("fastestFee").toString());
            return fastestFee;
        } catch (Exception e) {
            e.printStackTrace();
            return 0L;
        }
    }

3.测试

    //1.创建秘钥对
    @Test
    public void createPk() {
        Map resultMap = api.createWalletToJson();
        System.out.println(resultMap);
    }

    //2.离线签名交易
    @Test
    public void tranaction() throws Exception {
        api.setIsMainNet(false);
        String from = "monpT1RbuVuKJJocCfBruYbaYCVD6miprYx";
        String to = "mm26iTQBLEga8zJxvqURo81xuKE7y4m3hB";
        String fromPrivate = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
        Long amount = 1000l;
        List utxos = api.getUnspent(from);
        Long fee = api.getFee(amount,utxos);
        String sig = api.sign(from, to, fromPrivate, amount,fee, utxos);
        String txid = api.publishTx(sig);
        System.out.println(txid);
    }

你可能感兴趣的:(java开发,区块链开发)