前言:
需求:从公链拉取所有交易进行原始交易解析,匹配本地钱包地址根据交易确认数进行充值的确认。
方案:
初始化区块信息(高度、块hash等)持久化到数据库
根据区块hash从主链获取交易txs
解析txs对比充值钱包地址,存储充值信息、Unspent交易信息
更新数据库里的区块信息
进行线程的循环调用即可
1.初始化区块信息到数据库
@Order(value = 1)
@Service
public class BtcoinChargeServiceImpl implements CommandLineRunner {
private Logger LOG = LoggerFactory.getLogger("btcoin");
private static final String RECHARSTATE = "0";
private static final String CURRENTTYPE = "BTC";
//初始化区块信息
@Override
public void run(String... arg0) throws Exception {
//从数据读取信息进行判断
CoinBlockInfo blockInfo = blockMapper.selectByCoinType("BTC");
if (null == blockInfo || blockInfo.getBlockHeight() == 0) {
int blockCount = (int) btcService.getBlockCount();
String blockHash = btcService.getBlockHash(blockCount).toString();
LOG.info("=== [BTC] init block height is : {} and block hash is :{} start !! ===", blockCount, blockHash);
CoinBlockInfo blockInfos = new CoinBlockInfo();
blockInfos.setBlockHeight(blockCount); //高度
blockInfos.setBlockHash(blockHash);//块Hash
blockInfos.setCoinType(CURRENTTYPE);
blockInfos.setCreateTime(new Date());
blockInfos.setRemark("first record");
boolean res = blockMapper.insert(blockInfos) > 0 ? true : false;
if (res)
LOG.info("=== [BTC] init block success !!");
}
}
}
2. 区块hash从主链获取交易txs并进行解析
//比特币充值交易信息
@Service
public class BtcoinChargeServiceImpl implements BtcoinChargeService{
private Logger LOG = LoggerFactory.getLogger("btcoin");
private static final String RECHARSTATE = "0";
private static final String CURRENTTYPE = "BTC";
//对比确认次数
public int confirm = 9;
@Transactional(rollbackFor = CoinException.class)
@Override
public Object rechargeRecordFromBlockTx(List userList) throws CoinException {
Map result = new HashMap();
try {
// current block from mysql
CoinBlockInfo blockInfo = blockMapper.selectByCoinType("BTC");
int parseBlockCount = blockInfo.getBlockHeight();
int blockCount = (int) btcService.getBlockCount();
String blockHash = null;
if (parseBlockCount > 0) {
LOG.info("=== [BTC] the current BTC block height is {}, and the processing block height is {}. ===", blockCount, parseBlockCount);
if (blockCount > parseBlockCount) {
int current = parseBlockCount + 1;
if ((blockCount - parseBlockCount) >= confirm) {
try {
blockHash = btcService.getBlockHash(current).toString();
//LOG.info("=== [BTC] scan block [\theight:{}\t], [\thash:{}\t] begins !!! ===", current, blockHash);
if (parseTransactionInfo(userList, blockHash, current)) {
LOG.info("=== [BTC] end of scan block [\theight:{}\t], [\thash:{}\t] !!! ===", current, blockHash);
/* current height */
blockInfo.setBlockHeight(current);
blockInfo.setBlockHash(blockHash);
blockInfo.setUpdateTime(new Date());
if (blockMapper.updateByPrimaryKeySelective(blockInfo) > 0 ? true : false) {
LOG.info("=== [BTC-LOCAL] current block height: {}, previous block height: {} ===", current, parseBlockCount);
}
current++;
} /*else {
break;
}*/
} catch (CoinException e) {
LOG.info("=== [BTC] getting block information failed through block height.reason:{} ===", e.getMessage(), e);
throw new CoinException(e.getMessage());
}
}
}
}
} catch (CoinException e) {
LOG.info("=== com.wallet.bit.service.btc.impl.BtcoinChargeServiceImpl.rechargeRecordFromBlockTx(List):{} ===", e.getMessage(), e);
throw new CoinException(e.getMessage());
}
return result;
}
/*
* 对钱包地址进行比对处理
*/
private boolean parseTransactionInfo(List userList, String blockHash, int current) throws CoinException {
Map block = (Map) btcService.getblock(blockHash);
JSONObject blocks = JSONObject.parseObject(JSON.toJSONString(block));
if (isError(blocks)) {
LOG.info("=== [BTC] handling blockTransactions data errors !! ===");
return false;
}
JSONArray txs = JSONArray.parseArray(blocks.getString("tx"));
LOG.info("=== [BTC] scan block [\theight:{}\t], [\thash:{}\t], [\ttotal transactions:{}\t] begins !!! ===", current, blockHash, txs.size());
for (int i = 0, len = txs.size(); i < len; i++) {
String txId = txs.getString(i);
// deal block
//LOG.info("=== [BTC] scan block [\theight:{}\t], [\thash:{}\t], [\ttx:{}\t] begins !!! ===", current, blockHash, txId);
parseBlockInfoByTxId(userList, txId, current);
// LOG.info("=== [BTC] current blockheight:{},blockhash:{},txhash:{} operation completion!!", current, blockHash, txId);
}
return true;
}
/**
* 处理块信息
* @throws CoinException
* @Title: parseBlockInfoByTxId @param @param userList @param @param txId @param @throws CoinException 参数 @return void 返回类型 @throws
*/
private boolean parseBlockInfoByTxId(List userList, String txId, int height) throws CoinException {
try {
Map transaction = (Map) btcService.getTrawtransaction(txId, 1);
JSONObject info = JSONObject.parseObject(JSON.toJSONString(transaction));
Integer confirm = info.getInteger("confirmations");
Long time = info.getLong("time");
Long blocktime = info.getLong("blocktime");
// vin
JSONArray vins = JSONArray.parseArray(info.getString("vin"));
double sumvin = 0;
double sumvout = 0;
List formAddress = new ArrayList<>();
for (int z = 0, lenz = vins.size(); z < lenz; z++) {
JSONObject vin = JSONObject.parseObject(vins.getString(z));
String txid = vin.getString("txid");
if (null != txid) {
Integer vinN = vin.getInteger("vout");
// LOG.info("=== [BTC] search vins txid:{} trawtransaction! ===", txid);
Map parentTransaction = (Map) btcService.getTrawtransaction(txid, 1);
JSONObject parentInfo = JSONObject.parseObject(JSON.toJSONString(parentTransaction));
JSONArray vouts = JSONArray.parseArray(parentInfo.getString("vout"));
for (int j = 0, leg = vouts.size(); j < leg; j++) {
JSONObject vout = vouts.getJSONObject(j);
Integer n = vout.getInteger("n");
if (n == vinN) { // 收款金额
sumvin += vout.getDouble("value");
JSONObject scriptPubKey = vout.getJSONObject("scriptPubKey");
JSONArray addresses = scriptPubKey.getJSONArray("addresses");
formAddress.add(addresses.getString(0));
}
}
}
}
// vout
JSONArray vouts = JSONArray.parseArray(info.getString("vout"));
for (int x = 0, lenx = vouts.size(); x < lenx; x++) {
JSONObject vout = JSONObject.parseObject(vouts.getString(x));
Integer n = vout.getInteger("n");
JSONObject scriptPubKey = vout.getJSONObject("scriptPubKey");
String hex = scriptPubKey.getString("hex");
String type = scriptPubKey.getString("type");// usdt charge
if (!type.equals("nulldata")) {
if (n == 0) { // recharge = 0 , utxo
JSONArray addresses = scriptPubKey.getJSONArray("addresses");
String receviceAddr = addresses.getString(0);
for (UserAccount user : userList) {
Long userId = user.getUserId();
String userAddress = user.getUserAddress();
if (userAddress.equals(receviceAddr)) {
double amount = vout.getDouble("value");
if (amount > 0) {
// count fee
for (int w = 0, lenw = vouts.size(); w < lenw; w++) {
JSONObject feevout = JSONObject.parseObject(vouts.getString(x));
sumvout += feevout.getDouble("value");
}
try {
LOG.info("=== [BTC] handling transaction data entry and storage !!! ===");
//记录充值信息处理入库
} catch (Exception e) {
LOG.info("=== [BTC] record user recharge exception:{} ===", e.getMessage(), e);
}
}
}
}
}
}
}
} catch (CoinException e) {
LOG.info("=== com.wallet.bit.service.btc.impl.BtcoinChargeServiceImpl.parseBlockInfoByTxId(List, String, int):{} ===", e.getMessage(), e);
throw new CoinException(e.getMessage());
}
return false;
}
private boolean isError(JSONObject json) {
if (json == null || (StringUtils.isNotEmpty(json.getString("error")) && json.get("error") != "null")) {
return true;
}
return false;
}
}
通过JSON-rpc 进行节点的调用参考:https://blog.csdn.net/u011663149/article/details/87182097