定时任务扫描区块逻辑:
从redis获取已经同步的区块号 → 获取最新区块号 → 判断最新区块是否 > 已经同步的区块 + 1 →
大于则说明已经有完整的新区块可以进行扫描 → 扫描区块 → 判断是代币交易还是eth交易 →
如果是代币交易判断是否是转账操作(因为会有一些发币/授权/发合约…这种我们不需要的交易) → 判断to地址是否是系统内账户的(看具体项目
做可导入钱包那种项目就忽略这一步) → 计算交易金额 → 自行进行逻辑处理 → 保存刚同步的区块号到redis
返回数据的讲解:
先看一下api接口返回的数据结构
一般我们需要的字段hash input from to value
特别注意input 和
value字段
to是就是交易接收方的地址 代币交易时则为对应的合约地址
value是交易的eth金额
当交易为代币交易时value为0x0
input反过来 eth交易时input为0x 代币交易时是一个138位的十六进制编码
这个编码由三部分组成
1-34位为交易函数编码 例如transfer approve transferFrom等
35-74位为代币交易的实际的to地址 需要在前边拼接0x
75-138位是代币交易的十六进制的金额
transfer的十六进制编码为 0xa9059cbb
其他参考这里
ok明白这里接下来就很简单了
@Resource private IRedisService redisService; /** * 扫描区块定时任务--检查到账 */ @Scheduled(cron="0/10 * * * * ?") public void task(){ System.out.println("定时任务开始"); try { //获取当前同步的区块号 String block_re = redisService.getString(EthConstant.ETH_BLOCK_NUM); //获取最新区块号 BigInteger block_now = this.getBlockNum(); //初始化 if(block_re == null){ redisService.setString(EthConstant.ETH_BLOCK_NUM, block_now.toString()); return ; } BigInteger block = new BigInteger(block_re).add(new BigInteger("1")); if( block_now.compareTo(block) == 1 ){ //有新区块 List<JSONObject> list = this.getBlock(block.toString()); for (JSONObject json: list) { //交易from地址 String from = json.getString("from"); //交易的to地址 String to = json.getString("to"); String input = json.getString("input"); if(input.length() > 30){ //确定为代币交易 if(input.substring(0, 10).equals("0xa9059cbb")){ //确定是代币转账操作 那么to就是合约地址了 //代币交易to地址 String receive = input.substring(34, 40); receive = "0x" + receive; //交易hash String hash = json.getString("hash") //交易数量 已转为10进制的 目前单位的wei BigDecimal num = new BigDecimal(new BigInteger(input.substring(75), 16)); //合约地址 交易数量 收款账户 出账账户 交易hash 需要的信息都已拿到 剩下自行处理 } } else { //eth交易 EthAccountEntity account = accountService.getAccount(to); if(account != null){ //是系统内账户 String hash = json.getString("hash"); //交易数量单位是wei BigDecimal num = new BigDecimal(new BigInteger(json.getString("value"), 16)); //交易数量单位是ether BigDecimal numStr = Convert.fromWei(num, Convert.Unit.ETHER); //交易数量 收款账户 出账账户 交易hash 需要的信息都已拿到 剩下自行处理 } } } redisService.setString(EthConstant.ETH_BLOCK_NUM, block.toString()); } } catch (Exception e){ log.info("定时扫描出口异常", e); } } /** * 获取当前区块号 * @return * @throws Exception */ public BigInteger getBlockNum() throws Exception { StringBuffer path = new StringBuffer("/api?module=proxy&action=eth_blockNumber&apikey=").append(EthConstant.ETHERSCAN_API_KEY); HttpResponse response = HttpUtil.doGet("https://api.etherscan.io", path.toString(), "GET", headers, querys); String res = EntityUtils.toString(response.getEntity()); JSONObject json = JSONObject.parseObject(res); String result = json.getString("result"); return new BigInteger(result.substring(2), 16); } /** * 查询区块交易 * @param blockNum * @return * @throws Exception */ public List<JSONObject> getBlock(String blockNum) throws Exception { StringBuffer path = new StringBuffer("/api?module=proxy&action=eth_getBlockByNumber&tag=").append(blockNum) .append("&boolean=true&apikey=").append(EthConstant.ETHERSCAN_API_KEY); HttpResponse response = HttpUtil.doGet("https://api.etherscan.io", path.toString(), "GET", headers, querys); String res = EntityUtils.toString(response.getEntity()); JSONObject json = JSONObject.parseObject(res); JSONObject result = json.getJSONObject("result"); return JSON.parseArray(result.getString("transactions"), JSONObject.class); }
import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; import org.apache.http.client.HttpClient; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.conn.ClientConnectionManager; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.scheme.SchemeRegistry; import org.apache.http.conn.ssl.SSLSocketFactory; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.message.BasicNameValuePair; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.List; import java.util.Map; public class HttpUtil { public static HttpResponse doGet(String host, String path, String method, Map<String, String> headers, Map<String, String> querys) throws Exception { HttpClient httpClient = wrapClient(host); HttpGet request = new HttpGet(buildUrl(host, path, querys)); for (Map.Entry<String, String> e : headers.entrySet()) { request.addHeader(e.getKey(), e.getValue()); } return httpClient.execute(request); } public static HttpResponse doPost(String host, String path, String method, Map<String, String> headers, Map<String, String> querys, Map<String, String> bodys) throws Exception { HttpClient httpClient = wrapClient(host); HttpPost request = new HttpPost(buildUrl(host, path, querys)); for (Map.Entry<String, String> e : headers.entrySet()) { request.addHeader(e.getKey(), e.getValue()); } if (bodys != null) { List<NameValuePair> nameValuePairList = new ArrayList<NameValuePair>(); for (String key : bodys.keySet()) { nameValuePairList.add(new BasicNameValuePair(key, bodys.get(key))); } UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(nameValuePairList, "utf-8"); formEntity.setContentType("application/x-www-form-urlencoded; charset=UTF-8"); request.setEntity(formEntity); } return httpClient.execute(request); } private static String buildUrl(String host, String path, Map<String, String> querys) throws UnsupportedEncodingException { StringBuilder sbUrl = new StringBuilder(); sbUrl.append(host); if (!StringUtils.isBlank(path)) { sbUrl.append(path); } if (null != querys) { StringBuilder sbQuery = new StringBuilder(); for (Map.Entry<String, String> query : querys.entrySet()) { if (0 < sbQuery.length()) { sbQuery.append("&"); } if (StringUtils.isBlank(query.getKey()) && !StringUtils.isBlank(query.getValue())) { sbQuery.append(query.getValue()); } if (!StringUtils.isBlank(query.getKey())) { sbQuery.append(query.getKey()); if (!StringUtils.isBlank(query.getValue())) { sbQuery.append("="); sbQuery.append(URLEncoder.encode(query.getValue(), "utf-8")); } } } if (0 < sbQuery.length()) { sbUrl.append("?").append(sbQuery); } } return sbUrl.toString(); } private static HttpClient wrapClient(String host) { HttpClient httpClient = new DefaultHttpClient(); if (host.startsWith("https://")) { sslClient(httpClient); } return httpClient; } private static void sslClient(HttpClient httpClient) { try { SSLContext ctx = SSLContext.getInstance("TLS"); X509TrustManager tm = new X509TrustManager() { public X509Certificate[] getAcceptedIssuers() { return null; } public void checkClientTrusted(X509Certificate[] xcs, String str) { } public void checkServerTrusted(X509Certificate[] xcs, String str) { } }; ctx.init(null, new TrustManager[] { tm }, null); SSLSocketFactory ssf = new SSLSocketFactory(ctx); ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); ClientConnectionManager ccm = httpClient.getConnectionManager(); SchemeRegistry registry = ccm.getSchemeRegistry(); registry.register(new Scheme("https", 443, ssf)); } catch (KeyManagementException ex) { throw new RuntimeException(ex); } catch (NoSuchAlgorithmException ex) { throw new RuntimeException(ex); } } }