java开发以太坊---不搭建节点之扫描区块完成账户监听

置顶 精帖
0 3
大海无量
大海无量 博主 2020-12-22 19:50

定时任务扫描区块逻辑:
从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); } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106

工具类

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); } } } 
回帖
  • 消灭零回复