package io.mjoy.coin.service.tool;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.github.lihang941.common.http.HttpTool;
import com.github.lihang941.common.utils.RandomCode;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author : [email protected]
* @since : 2018/10/13
*/
public class BitCoinRpcTool {
private static Logger logger = LoggerFactory.getLogger(BitCoinRpcTool.class);
public static class BtcRpcException extends IOException {
public JSONObject res;
public BtcRpcException(JSONObject res) {
super(JSON.toJSONString(res, SerializerFeature.PrettyFormat));
this.res = res;
}
public JSONObject getRes() {
return res;
}
public String toJson() {
return "result error: \n" + JSON.toJSONString(res, SerializerFeature.PrettyFormat);
}
}
private HttpTool httpTool;
private String userName;
private String password;
private String apiUrl;
public BitCoinRpcTool(String userName, String password, String apiUrl, OkHttpClient okHttpClient) {
this.userName = userName;
this.password = password;
this.apiUrl = apiUrl;
this.httpTool = new HttpTool(okHttpClient);
}
public JSONObject sendPost(String method, Object... param) throws IOException {
String auth = Base64.encodeBase64String((userName + ":" + password).getBytes());
JSONObject json = httpTool.execute(new Request.Builder().url(apiUrl)
.header("Authorization", "Basic " + auth)
.post(RequestBody.create(
MediaType.parse("application/json; charset=utf-8"),
new JSONObject()
.fluentPut("id", RandomCode.getUUIDString())
.fluentPut("jsonrpc", "2.0")
.fluentPut("method", method)
.fluentPut("params", param)
.toJSONString()
)).build(), HttpTool.jsonObjectResponseHandle(), res -> {
if (!Arrays.asList(200, 500).contains(res.code())) {
throw new IOException("code不正确");
}
});
if (isError(json)) {
throw new BtcRpcException(json);
}
return json;
}
private boolean isError(JSONObject json) {
if (json == null) {
return true;
}
return json.get("error") != null && !StringUtils.endsWith("null", json.getString("error"));
}
// 获取btc余额
public BigDecimal getBalance() throws IOException {
JSONObject res = sendPost("getbalance");
return res.getBigDecimal("result");
}
// 判断节点是否运行
public boolean getNetworkInfo() throws IOException {
JSONObject res = sendPost("getnetworkinfo");
return res.getJSONObject("result") != null;
}
// 获取新的地址
public String getNewAddress() throws IOException {
JSONObject res = sendPost("getnewaddress");
return res.getString("result");
}
// 获取区块信息
public JSONObject getBlockChainInfo() throws IOException {
JSONObject res = sendPost("getblockchaininfo");
return res.getJSONObject("result");
}
// 校验地址
public JSONObject validateAddress(String address) throws IOException {
JSONObject res = sendPost("validateaddress", address);
return res.getJSONObject("result");
}
// 转账
public JSONObject sendToAddress(String address, BigDecimal value) throws IOException {
JSONObject res = sendPost("sendtoaddress", address, value);
return res;
}
// 获取块高
public BigInteger getBlockCount() throws IOException {
JSONObject res = sendPost("getblockcount");
return res.getBigInteger("result");
}
// 获取指定区块HASH
public String getBlockHash(BigInteger height) throws IOException {
JSONObject res = sendPost("getblockhash", height);
return res.getString("result");
}
//获取指定区块hash详情
public JSONObject getBlock(String hash) throws IOException {
JSONObject res = sendPost("getblock", hash);
return res.getJSONObject("result");
}
//获取交易详情
public JSONObject getTransaction(String txHash) throws IOException {
JSONObject res = sendPost("gettransaction", txHash);
return res.getJSONObject("result");
}
// 获取转账记录
public
List list = new ArrayList<>();
JSONObject block = this.getBlock(this.getBlockHash(height));
JSONArray txArr = block.getJSONArray("tx");
for (int i = 0; i < txArr.size(); i++) {
String txHash = txArr.getString(i);
// 判断该hash是否处理过
try {
JSONObject transaction = this.getTransaction(txHash);
JSONArray jsonArrayVout = transaction.getJSONArray("details");
if (jsonArrayVout != null || jsonArrayVout.size() > 0) {
for (int i1 = 0; i1 < jsonArrayVout.size(); i1++) {
JSONObject jsonVout = jsonArrayVout.getJSONObject(i1);
BigDecimal value = jsonVout.getBigDecimal("amount");
String category = jsonVout.getString("category");
if (value.compareTo(BigDecimal.ZERO) == 1 && "receive".equals(category)) {
String address = jsonVout.getString("address");
list.add(new BitTransaction().setValue(value).setAddress(address).setHash(txHash).setDetail(transaction).setCategory(category));
}
}
}
} catch (BitCoinRpcTool.BtcRpcException e) {
// hash略过
if (e.getRes().get("error") != null && StringUtils.equals("Invalid or non-wallet transaction id", e.getRes().getJSONObject("error").getString("message"))) {
continue;
} else {
logger.warn("未知的交易轮询错误 hash :{} \n error: {}", txHash, e.toJson());
}
}
}
return list;
}
public static class BitTransaction {
private BigDecimal value;
private String address;
private String category;
private String hash;
private JSONObject detail;
public BigDecimal getValue() {
return value;
}
public BitTransaction setValue(BigDecimal value) {
this.value = value;
return this;
}
public String getAddress() {
return address;
}
public BitTransaction setAddress(String address) {
this.address = address;
return this;
}
public String getCategory() {
return category;
}
public BitTransaction setCategory(String category) {
this.category = category;
return this;
}
public JSONObject getDetail() {
return detail;
}
public BitTransaction setDetail(JSONObject detail) {
this.detail = detail;
return this;
}
public String getHash() {
return hash;
}
public BitTransaction setHash(String hash) {
this.hash = hash;
return this;
}
}
}