以太坊消息订阅
一、功能
- 订阅某个账户地址(也可以是合约地址)发出的交易
- 订阅某个账户地址(也可以是合约地址)收到的交易
- 可设置过滤条件 ,如:合约中具体方法名、交易金额、哪个块开始
支持以太坊消息订阅
下文基于github开源项目进行实践:https://github.com/Magician-Blockchain/Magician-Scanning
二、导入依赖
日志依赖
<dependency>
<groupId>com.github.yuyenews</groupId>
<artifactId>Magician-Scanning</artifactId>
<version>1.0.12</version>
</dependency>
<!-- This is the logging package, you must have it or the console will not see anything, any logging package that can bridge with slf4j is supported -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>1.7.12</version>
</dependency>
web3j依赖
<dependency>
<groupId>org.web3j</groupId>
<artifactId>core</artifactId>
<version>4.9.8</version>
</dependency>
<dependency>
<groupId>org.web3j</groupId>
<artifactId>codegen</artifactId>
<version>4.9.8</version>
</dependency>
三、具体实现步骤
创建监听器
监听器可以创建多个,根据您的需求分别设置监听条件
/**
* 创建一个类,实现 EthMonitorEvent接口 即可
*/
public class EventDemo implements EthMonitorEvent {
/**
* 筛选条件,如果遇到了符合条件的交易,会自动触发 call方法
* 这些条件都是 并且的关系,必须要同时满足才行
* 如果不想根据某个条件筛选,直接不给那个条件设置值就好了
* 这个方法如果不实现,或者返回null, 那么就代表监听任意交易
*/
@Override
public EthMonitorFilter ethMonitorFilter() {
return EthMonitorFilter.builder()
.setFromAddress("0x131231249813d334C58f2757037F68E2963C4crc") // 筛选 fromAddress 发送的交易
.setToAddress("0x552115849813d334C58f2757037F68E2963C4c5e") // 筛选 toAddress 或 合约地址 收到的交易
.setMinValue(BigInteger.valueOf(1)) // 筛选发送的主链币数量 >= minValue 的交易
.setMaxValue(BigInteger.valueOf(10)) // 筛选发送的主链币数量 <= maxValue 的交易
.setInputDataFilter( // 根据inputData筛选
InputDataFilter.builder()
.setFunctionCode(ERC20.TRANSFER.getFunctionCode()) // 函数签名(被调用的合约内的某方法), 支持任意函数,这里的枚举只是一部分标准的合约函数
.setTypeReferences( // 此方法的参数列表(仅类型)
new TypeReference<Address>(){},
new TypeReference<Uint256>(){}
)
.setValue("0x552115849813d334C58f2757037F68E2963C4c5e", null)// 筛选第几个参数 = 什么值
);
}
/**
* 如果遇到了符合上面条件的交易,就会触发这个方法
* transactionModel.getEthTransactionModel() 是一个交易对象,内部包含hash,value,from,to 等 所有的数据
*/
@Override
public void call(TransactionModel transactionModel) {
// 符合条件的交易记录
EthBlock.TransactionObject transactionObject = transactionModel.getEthTransactionModel().getTransactionObject();
// 本条交易记录所在的块信息
EthBlock ethBlock = transactionModel.getEthTransactionModel().getEthBlock();
}
}
输入数据过滤器详细解
如果您想监控某合约内的某函数,则被调用的交易
public EthMonitorFilter ethMonitorFilter() {
return EthMonitorFilter.builder()
.setToAddress("0x552115849813d334C58f2757037F68E2963C4c5e") // 合约地址
.setInputDataFilter( // 根据inputData筛选
InputDataFilter.builder()
.setFunctionCode("0xadasasdf") // 被调用的函数编码(inputData前十位)
);
}
如果有一个契约[0x552115849813d334C58f2757037F68E2963C4c5e],里面有一个函数是transferFrom(address from, address to, uint256 amount)
你想实现一个监控:如果有人用这个合约里的这个函数,将代币转给[0x552115849813d334C58f2757037F68E2963C4c5e]时,就触发Monitor事件,那么你可以这样写
public EthMonitorFilter ethMonitorFilter() {
return EthMonitorFilter.builder()
.setToAddress("0x552115849813d334C58f2757037F68E2963C4c5e") // 合约地址
.setInputDataFilter( // 根据inputData筛选
InputDataFilter.builder()
.setFunctionCode(ERC20.TRANSFER_FROM.getFunctionCode()) // 被调用的函数编码(inputData前十位)
.setTypeReferences( // 此方法的参数列表(仅类型)
new TypeReference<Address>(){}, // 第一个参数的类型
new TypeReference<Address>(){}, // 第二个参数的类型
new TypeReference<Uint256>(){} // 第三个参数的类型
)
.setValue(null, "0x552115849813d334C58f2757037F68E2963C4c5e", null)// 筛选第二个参数(to) = 0x552115849813d334C58f2757037F68E2963C4c5e
);
}
开线程扫描区块链上交易消息
EventThreadPool.init(1);
// 开启一个扫块任务,如果你想扫描多个链,那么直接拷贝这段代码,并修改配置即可
MagicianBlockchainScan.create()
.setRpcUrl(
EthRpcInit.create()
.addRpcUrl("https://data-seed-prebsc-1-s1.binance.org:8545")
) // 节点的RPC地址
.setScanPeriod(5000) // 间隔多久,扫描下一个区块
.setBeginBlockNumber(BigInteger.valueOf(24318610)) // 从哪个块高开始扫描
.setEndBlockNumber(BigInteger.valueOf(24318680)) // 扫描到哪个块高就停止这个任务(不设置,或者设置为0,代表不限制)
.addEthMonitorEvent(new EventOne()) // 添加 监听事件
.addEthMonitorEvent(new EventTwo()) // 添加 监听事件
.addEthMonitorEvent(new EventThree()) // 添加 监听事件
.start();
使用代理访问RPC地址
// 使用 addRpcUrl 方法的另一个重载,传入代理设置即可
EthRpcInit.create()
.addRpcUrl("https://data-seed-prebsc-1-s1.binance.org:8545/",
new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 4780)))
// ---------- 除了上面那种以外,addRpcUrl 方法一共有这么几种重载,根据你的需求挑选合适的方法 ----------
// 直接传入 wei3j的HttpService
// 这种方法 可定制化最高,基本上就是web3j本来的使用方式
EthRpcInit.create()
.addRpcUrl(new HttpService(""))
// 传入okHttpClient
// 这种方法 可定制化程度也非常高,基本上就是使用okHttp访问 区块链节点了
OkHttpClient okHttpClient = xxxxxx;
EthRpcInit.create()
.addRpcUrl(okHttpClient)
// 有些代理服务器 需要鉴权,可以使用这种方式来设置用户名和密码
EthRpcInit.create()
.addRpcUrl("https://data-seed-prebsc-1-s1.binance.org:8545/",
new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 4780)),
(Route route, Response response) -> {
//设置代理服务器账号密码
String credential = Credentials.basic("用户名", "密码");
return response.request().newBuilder()
.header("Proxy-Authorization", credential)
.build();
}
)
四、其他常用功能
1、获取交易内容:transactionModel.getEthTransactionModel().getTransactionObject().getInput()
public class EventDemo implements EthMonitorEvent {
/**
* 筛选条件,如果遇到了符合条件的交易,会自动触发 call方法
* 这些条件都是 并且的关系,必须要同时满足才行
* 如果不想根据某个条件筛选,直接不给那个条件设置值就好了
* 这个方法如果不实现,或者返回null, 那么就代表监听任意交易
*/
@Override
public EthMonitorFilter ethMonitorFilter() {
return EthMonitorFilter.builder()
.setFromAddress("0xD80E9752fb271A0F17eA6a3B5Bda33f0a0d467E3") // 筛选 fromAddress 发送的交易
.setToAddress("0xb72d14E3bE3c288d046B0d7DD022D40B0c7ea2D9"); // 筛选 toAddress 或 合约地址 收到的交易
}
/**
* 如果遇到了符合上面条件的交易,就会触发这个方法
* transactionModel.getEthTransactionModel() 是一个交易对象,内部包含hash,value,from,to 等 所有的数据
*/
@Override
public void call(TransactionModel transactionModel) {
// 符合条件的交易记录
EthBlock.TransactionObject transactionObject = transactionModel.getEthTransactionModel().getTransactionObject();
// 本条交易记录所在的块信息
EthBlock ethBlock = transactionModel.getEthTransactionModel().getEthBlock();
System.out.println(transactionModel.getEthTransactionModel().getTransactionObject().getInput());
}
}
2、停止某块扫描任务:blockChainScan.shutdown();
3、获取当前链的最大高度块:blockChainScan.getCurrentBlockHeight();
4、配置多个RPC URL 实际负载均衡
三条链都是一样的,只是EthRpcInit需要改成对应的链的类
调用addRpcUrl方法多次,创建多个RPC URL,即可实现负载均衡(轮询)
MagicianBlockchainScan.create()
.setRpcUrl(
EthRpcInit.create()
.addRpcUrl("https://data-seed-prebsc-1-s1.binance.org:8545")
.addRpcUrl("https://data-seed-prebsc-2-s1.binance.org:8545")
.addRpcUrl("https://data-seed-prebsc-1-s2.binance.org:8545")
) // 节点的RPC地址
五、功能演示
1、部署智能合约
智能合约
智能合约地址
在消息订阅中绑定
2、以以太坊私有链交互
开启消息订阅
存储数字1
可以看到新加的区块为第9个块,区块内容为131
将存储的数值设定为2,新增了一个区块,并且区块内容为132
第12个块是与绑定的账号消息无关的区块,该信息不会提取