java实现比特币钱包_Java 开发 BTC 钱包

一.比特币离线地址和私钥生成

下面是 BTC  离线生成地址和私钥的代码,废话不多说,直接上代码

package com.gingernet.bitcoin;

import com.gingernet.utils.Utils;

import org.bitcoinj.core.Base58;

import org.bitcoinj.core.ECKey;

import org.bitcoinj.core.NetworkParameters;

import org.bitcoinj.crypto.HDUtils;

import org.bitcoinj.params.MainNetParams;

import org.bitcoinj.params.TestNet3Params;

import org.bitcoinj.wallet.DeterministicKeyChain;

import org.bitcoinj.wallet.DeterministicSeed;

import org.bitcoinj.wallet.UnreadableWalletException;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.spongycastle.crypto.digests.RIPEMD160Digest;

import java.math.BigInteger;

import java.security.*;

import java.security.interfaces.ECPrivateKey;

import java.security.interfaces.ECPublicKey;

import java.security.spec.ECGenParameterSpec;

import java.security.spec.ECPoint;

import java.util.HashMap;

import java.util.Map;

public class Address {

private Logger logger = LoggerFactory.getLogger(getClass());

static NetworkParameters params;

// 生成 WIF 格式的地址(中心化钱包使用)

public Map generateBtcAddress() {

NetworkParameters paramsTest = TestNet3Params.get();

//NetworkParameters params = MainNetParams.get();

ECKey key = new ECKey();

Map btcMap = new HashMap<>();

btcMap.put("btcWifPk", key.getPrivateKeyAsWiF(paramsTest));

btcMap.put("btcPk", key.getPrivateKeyAsHex());

btcMap.put("btcPuKey", key.getPublicKeyAsHex());

btcMap.put("btcAddress", key.toAddress(paramsTest).toString());

return btcMap;

}

// 由助记词生成地址流程(去中心化钱包使用)

public String CreateAddressByWord(String wordsList) throws Exception {

NetworkParameters params  = TestNet3Params.get();

DeterministicSeed deterministicSeed = new DeterministicSeed(wordsList, null, "", 0L);

DeterministicKeyChain deterministicKeyChain = DeterministicKeyChain.builder().seed(deterministicSeed).build();

BigInteger privKey = deterministicKeyChain.getKeyByPath(HDUtils.parsePath("44H / 1H / 0H / 0 / 2"), true).getPrivKey();

ECKey ecKey = ECKey.fromPrivate(privKey);

org.bitcoinj.core.Address address = ecKey.toAddress(params);

System.out.println(ecKey.getPrivateKeyAsWiF(params));

return address.toBase58();

}

// 比特币系列地址生成流程

public String bitcoinS(String version) {

try {

KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");

ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp256k1");

keyGen.initialize(ecSpec);

KeyPair kp = keyGen.generateKeyPair();

PublicKey pub = kp.getPublic();

PrivateKey pvt = kp.getPrivate();

ECPrivateKey epvt = (ECPrivateKey) pvt;

String sepvt = Utils.adjustTo64(epvt.getS().toString(16)).toUpperCase();

logger.warn("s[" + sepvt.length() + "]: " + sepvt);

logger.warn("私钥{}", sepvt);

ECPublicKey epub = (ECPublicKey) pub;

ECPoint pt = epub.getW();

String sx = Utils.adjustTo64(pt.getAffineX().toString(16)).toUpperCase();

String sy = Utils.adjustTo64(pt.getAffineY().toString(16)).toUpperCase();

String bcPub = "04" + sx + sy;

logger.warn("公钥{}", bcPub);

MessageDigest sha = MessageDigest.getInstance("SHA-256");

byte[] s1 = sha.digest(bcPub.getBytes("UTF-8"));

logger.warn("sha256后{}", Utils.byte2Hex(s1).toUpperCase());

RIPEMD160Digest digest = new RIPEMD160Digest();

digest.update(s1, 0, s1.length);

byte[] ripemd160Bytes = new byte[digest.getDigestSize()];

digest.doFinal(ripemd160Bytes, 0);

logger.warn("ripemd160加密后{}", Utils.bytesToHexString(ripemd160Bytes));

byte[] networkID = new BigInteger(version, 16).toByteArray();

byte[] extendedRipemd160Bytes = Utils.add(networkID, ripemd160Bytes);

logger.warn("添加NetworkID后{}", Utils.bytesToHexString(extendedRipemd160Bytes));

byte[] twiceSha256Bytes = Utils.sha256(Utils.sha256(extendedRipemd160Bytes));

logger.warn("两次sha256加密后{}", Utils.bytesToHexString(twiceSha256Bytes));

byte[] checksum = new byte[4];

System.arraycopy(twiceSha256Bytes, 0, checksum, 0, 4);

logger.warn("checksum{}", Utils.bytesToHexString(checksum));

byte[] binaryBitcoinAddressBytes = Utils.add(extendedRipemd160Bytes, checksum);

logger.warn("添加checksum之后{}", Utils.bytesToHexString(binaryBitcoinAddressBytes));

String ltccoinAddress = Base58.encode(binaryBitcoinAddressBytes);

logger.warn("地址{}", ltccoinAddress);

return ltccoinAddress;

} catch (Exception e) {

e.printStackTrace();

}

return "";

}

}

二. 比特币交易签名

获取手续费

查看比特币建议手续费的网站https://bitcoinfees.earn.com/

在上面这个网站上可以看到当前的手续费是多少聪每比特,一般来说,一个中等的交易是225比特,手续费的结果是8550聪。但是许多钱包使用每千字节satoshis或每千字节比特币,因此您可能需要转换单位。

此处显示的费用为每字节交易数据的Satoshis(0.00000001 BTC)。 矿工通常首先包括费用/字节最高的交易。钱包应根据用户需要确认的速度,根据此数字进行费用计算。

也可以通过其他建议手续费网站获取,或者通过节点获取

下面是离线签名的代码,下面代码是测试网络的,若需要主网测试,直接替换即可

package com.gingernet.bitcoin;

import java.text.Collator;

import java.util.ArrayList;

import java.util.List;

import com.gingernet.api.po.UnSpentUtxo;

import org.apache.commons.codec.binary.Hex;

import org.apache.commons.configuration2.Configuration;

import org.bitcoinj.core.Address;

import org.bitcoinj.core.Coin;

import org.bitcoinj.core.Context;

import org.bitcoinj.core.DumpedPrivateKey;

import org.bitcoinj.core.ECKey;

import org.bitcoinj.core.NetworkParameters;

import org.bitcoinj.core.Sha256Hash;

import org.bitcoinj.core.Transaction;

import org.bitcoinj.core.TransactionOutPoint;

import org.bitcoinj.core.UTXO;

import org.bitcoinj.core.Utils;

import org.bitcoinj.params.MainNetParams;

import org.bitcoinj.params.TestNet3Params;

import org.bitcoinj.script.Script;

import org.omg.CORBA.UNKNOWN;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;

import org.bitcoinj.core.TransactionConfidence;

public class TransactionSign {

private static Logger LOG = LoggerFactory.getLogger(TransactionSign.class);

static NetworkParameters params;

static {

try {

params = TestNet3Params.get(); // MainNetParams.get();

LOG.info("=== [BTC] bitcoin  client networkID:{} ===", params.getId());

} catch (Exception e) {

LOG.info("=== [BTC] com.bscoin.coldwallet.cointype.btc.rawtransaction:{} ===", e.getMessage(), e);

}

}

public String SignTransaction(String privKey, String recevieAddr, String formAddr, long amount, long fee, List unUtxos) {

if(!unUtxos.isEmpty() && null != unUtxos){

List utxos = new ArrayList();

DumpedPrivateKey dumpedPrivateKey = DumpedPrivateKey.fromBase58(params, privKey);

ECKey key = dumpedPrivateKey.getKey();

// 接收地址

Address receiveAddress = Address.fromBase58(params, recevieAddr);

// 构建交易

Transaction tx = new Transaction(params);

tx.addOutput(Coin.valueOf(amount), receiveAddress);

// 如果需要找零 消费列表总金额 - 已经转账的金额 - 手续费

long value = unUtxos.stream().mapToLong(UnSpentUtxo::getValue).sum();

Address toAddress = Address.fromBase58(params, formAddr);

long leave  = value - amount - fee;

if(leave > 0){

tx.addOutput(Coin.valueOf(leave), toAddress);

}

// utxos is an array of inputs from my wallet

for (UnSpentUtxo unUtxo : unUtxos) {

utxos.add(new UTXO(Sha256Hash.wrap(unUtxo.getHash()),

unUtxo.getTxN(),

Coin.valueOf(unUtxo.getValue()),

unUtxo.getHeight(),

false,

new Script(Utils.HEX.decode(unUtxo.getScript())),

unUtxo.getAddress()));

}

for (UTXO utxo : utxos) {

TransactionOutPoint outPoint = new TransactionOutPoint(params, utxo.getIndex(), utxo.getHash());

tx.addSignedInput(outPoint, utxo.getScript(), key, Transaction.SigHash.ALL, true);

}

Context context = new Context(params);

tx.getConfidence().setSource(TransactionConfidence.Source.NETWORK);

tx.setPurpose(Transaction.Purpose.USER_PAYMENT);

LOG.info("Bitcoin Sign Success :{} ===",tx.getHashAsString());

return new String(Hex.encodeHex(tx.bitcoinSerialize()));

}

return null;

}

}

三. 发送比特币交易到区块链网络

// 通过第三方接口发送交易到 BTC 网络

public String SendBtcRawTx(String data) throws Exception {

String txid = "";

try {

RestTemplate restTemplate = new RestTemplate();

HttpHeaders headers = new HttpHeaders();

headers.add("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36");

headers.setContentType(MediaType.APPLICATION_JSON_UTF8);

String requestBody = "{\"tx\":\"" + data + "\"}";

HttpEntity request = new HttpEntity(requestBody, headers);

ResponseEntity response = restTemplate.exchange("https://api.blockcypher.com/v1/btc/main/txs/push?token=5f4eb6f1fa3d4f6fa519ee93a5a3fb2e", HttpMethod.POST, request, String.class);

System.out.println(response.getBody());

String rsp = response.getBody();

JSONObject jsonObject = (JSONObject) JSONValue.parse(rsp);

JSONObject txObj = (JSONObject)jsonObject.get("tx");

txid = txObj.get("hash").toString();

} catch (Exception e) {

e.printStackTrace();

}

return txid;

}

四. 确认转账交易是否成功

流程:扫快获取到交易 Hash, 根据交易Hash 获取到交易,解码交易得到 Vout 和 Vin,根据业务需求入口。

用到的接口有:getblockchaininfo, getchaintips, decoderawtransaction 等接口

原创文章,作者:[email protected],如若转载,请注明出处:http://www.hwjiao.com/2021/03/05/java-%e5%bc%80%e5%8f%91-btc-%e9%92%b1%e5%8c%85/

你可能感兴趣的:(java实现比特币钱包)