文章目录
用到的库
implementation’org.bitcoinj:bitcoinj-core:0.15.2’
implementation(‘com.alibaba:fastjson:1.2.51’)
创建钱包(由于和eth共有助记词就用助记词创建)
fun importByWords(words: String): ECKey {
val wordsList = words.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray().toList()
val deterministicSeed = DeterministicSeed(wordsList, null, "", 0L)
val deterministicKeyChain = DeterministicKeyChain.builder().seed(deterministicSeed).build()
// //如果是调试模式, 第二个字符串应该填1' M/44H/0H/0H/0/0
val privKeyBTC = deterministicKeyChain.getKeyByPath(parsePath("M/44H/0H/0H"), true).privKey
//拿到私钥就可以创建钱包了
return ECKey.fromPrivate(privKeyBTC)
}
拿到钱包信息(上面获取到的)
获取地址
LegacyAddress.fromKey(params, key).toBase58()
获取私钥
key.getPrivateKeyEncoded(params).toBase58()
查询余额和交易记录
https://blockchain.info/rawaddr/1HMrjkcrqV6ZewxnNcCc7WgNa4T8zWRpu1
获取手续费(可用可不用,转账的时候可以写死手续费)
/**
* 获取矿工费用
*
* @param amount
* @param utxos
* @return
*/
public static Long getFee(long amount, List<UTXO> 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;
//其实34乘以几都行,加10也可以不要,无非就是手续费低点,打包确认慢点
// fee = (utxoSize * 148 * 34 * 3 + 10) * feeRate;
fee = (utxoSize * 148 * 34 * 3 + 10) * feeRate;
}
}
return fee;
}
/**
* 获取btc费率
*
* @return
*/
public static Long getFeeRate() {
try {
String httpGet1 = get("https://bitcoinfees.earn.com/api/v1/fees/recommended");
Map map = JSON.parseObject(httpGet1, Map.class);
Long fastestFee = Long.valueOf(map.get("hourFee").toString());
return fastestFee;
} catch (Exception e) {
e.printStackTrace();
return 0L;
}
}
获取未消费列表(自己的钞票面值不可分割)
举例说明不可分割,你手里面有4张5元的,你买东西花了12 ,你需要给3张5元的,找回3元
https://blockchain.info/unspent?active=1HMrjkcrqV6ZewxnNcCc7WgNa4T8zWRpu1
https://api-r.bitcoinchain.com/v1/address/utxo/1Ccb4vrC5atJHHdj3hHhYwJmYxtVZLZXWc
计算
1.算出自己需要转出去的值 自己的几个面值刚好大于sendAmount+fee
2.需要找回的钱 减去sendAmount和fee
组合交易信息
1.添加转出金额
2.添加找回金额
3.输入未消费列表项(签名)
4.序列化并且十六进制
发送 签名数据 到链上
https://blockchain.info/pushtx post请求 参数tx
转账代码
/**
* btc交易签名
*
* @param toAddress
* @param amount
* @return
* @throws Exception
*/
public static Pair<Boolean, String> sign(String toAddress, long amount) throws Exception {
String fromAddress = UserInfoData.Companion.getInstance().getBtcAddress();
String changeAddress = fromAddress;//找零地址
List<UTXO> utxos = getUnspent(fromAddress);
NetworkParameters networkParameters = MainNetParams.get();
Transaction transaction = new Transaction(networkParameters);
String privateKey = EthUtilsKt.getDecryptAESPriKey();
Long changeAmount = 0L;
Long utxoAmount = 0L;
//long fee = 8000;
//long fee = getFee(amount, utxos);
long fee = 8000L;
List<UTXO> needUtxos = new ArrayList<>();
//获取未消费列表
if (utxos == null || utxos.size() == 0) {
throw new Exception(getString(R.string.network_tip));
}
//遍历未花费列表,组装合适的item
for (UTXO utxo : utxos) {
if (utxoAmount >= (amount + fee)) {
break;
} else {
needUtxos.add(utxo);
utxoAmount += utxo.getValue().value;
}
}
transaction.addOutput(Coin.valueOf(amount), Address.fromString(networkParameters, toAddress));
//消费列表总金额 - 已经转账的金额 - 手续费 就等于需要返回给自己的金额了
changeAmount = utxoAmount - (amount + fee);
//余额判断
if (changeAmount < 0) {
throw new Exception(getString(R.string.error_balance_not_enough));
}
//输出-转给自己(找零)
if (changeAmount > 0) {
transaction.addOutput(Coin.valueOf(changeAmount), Address.fromString(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());
LogUtil.e("fee:{},utxoAmount:{},changeAmount{}", "" + fee + utxoAmount + changeAmount);
LogUtil.e(hash);
return new Pair(true, hash);
}
/***
* 获取未消费列表
* @param address :地址
* @return
*/
public static List<UTXO> getUnspent(String address) {
List<UTXO> utxos = new ArrayList();
String host = "blockchain.info";
String url = "https://" + host + "/zh-cn/unspent?active=" + address;
try {
String httpGet = get(url);//TODO;联网
if ("No free outputs to spend".equals(httpGet)) {
return utxos;
}
JSONObject jsonObject = JSON.parseObject(httpGet);
JSONArray unspentOutputs = jsonObject.getJSONArray("unspent_outputs");
List<Map> 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) {
LogUtil.e("【BTC获取未消费列表】失败,", e.getMessage());
return null;
}
}