Contract Event Push¶
Tags: “java-sdk” “event subscription” “Event”
1. Function Introduction¶
The contract event push function provides an asynchronous push mechanism for contract events. The client sends a registration request to the node, which carries the parameters of the contract events that the client is concerned about. The node filters the ‘Event Log’ of the request block range according to the request parameters and pushes the results to the client in stages。
2. Interactive Protocol¶
The interaction between client and node is based on WebSocket protocol。Interaction is divided into three stages: registration request, node reply, ‘Event Log’ data push。
2.1 Registration Request¶
The client sends a registration request for event push to the node:
// request sample:
{
"fromBlock": "latest",
"toBlock": "latest",
"addresses": [
"0xca5ed56862869c25da0bdf186e634aac6c6361ee"
],
"topics": [
"0x91c95f04198617c60eaf2180fbca88fc192db379657df0e412a9f7dd4ebbe95d"
],
"groupID": "group0",
"filterID": "bb31e4ec086c48e18f21cb994e2e5967"
}
-filerID: string type, unique for each request, marking a registration task -groupID: string type, group ID -fromBlock: shaping string, initial block。”latest” current block high -toBlock: shaping string, final block。When “latest” is processed to the current block high, continue to wait for a new block -addresses: string or string array: string represents a single contract address, array is multiple contract addresses, array can be empty -topics: string type or array type: string represents a single topic, array is multiple topics, array can be empty
2.2 Node reply¶
When the node accepts the client registration request, it checks the request parameters and replies to the client whether it has successfully accepted the registration request。
// response sample:
{
"filterID": "bb31e4ec086c48e18f21cb994e2e5967",
"result": 0
}
-filterID: string type, unique for each request, marking a registration task -result: shaping, return result。0 success, the rest are failure status codes
2.3 Event Log data push¶
After the node verifies that the client registration request is successful, it pushes the ‘Event Log’ data to the client based on the client request parameters。
// event log push sample:
{
"filterID": "bb31e4ec086c48e18f21cb994e2e5967",
"result": 0,
"logs": [
]
}
-filterID: string type, unique for each request, marking a registration task -result: shaping 0: ‘Event Log’ data push 1: push complete。The client registers and requests the data push of the corresponding node multiple times (the request block range is relatively large or waiting for a new block). If the ‘result’ field is 1, the node push has ended -logs: Log object array, valid when result is 0
3. Java SDK Contract Event Tutorial¶
Registration Interface¶
The ‘org.fisco.bcos.sdk.v3.eventsub.EventSubscribe’ class in the Java SDK provides an interface for registering contract events. You can call ‘subscribeEvent’ to send a registration request to a node and set a callback function。
public String subscribeEvent(EventSubParams params, EventSubCallback callback);
‘params’ register parameter¶
Parameters for event callback request registration:
public class EventSubParams {
private BigInteger fromBlock;
private BigInteger toBlock;
private List<String> addresses;
private List<List<String>> topics;
}
‘callback ‘callback object¶
public interface EventSubCallback {
void onReceiveLog(String eventSubId, int status, List<EventLog> logs);
}
‘status’ callback return status:
0 : Normal push. Logs is the event log pushed by the node
1 : The push is completed, and all blocks in the execution interval have been processed
42000 : Other errors
-41000 : Invalid parameter, client validation parameter error returned
-41001 : Parameter error, node validation parameter error returned
-41002 : Group does not exist
-41003 : Block interval of request error
-41004 : Error in node push data format
-41005 : Request Send Timeout
-41006 : Client has no subscription rights
-41007 : Event not registered, unsubscribe failed
‘logs’ indicates the list of ‘Event Log’ objects of the callback. The status is valid as 0。The default value is’ null ‘. The’ data ‘field of the following EventLog object can be resolved in the subclass through’ org.fisco.bcos.sdk.v3.abi.ContractCodec ‘。
/ / EventLog object
public class EventLog {
private String logIndex;
private String transactionIndex;
private String transactionHash;
private String blockNumber;
private String address;
private String data;
private List<String> topics;
}
Implement callback object
Java SDK has no default implementation for the callback class’ EventSubCallback ‘. You can inherit the’ EventSubCallback ‘class and rewrite the’ onReceiveLog ‘interface to implement your own callback logic processing。
class SubscribeCallback implements EventSubCallback {
public void onReceiveLog(String eventSubId, int status, List<EventLog> logs) {
// ADD CODE
}
}
Note: The logs of multiple callbacks of the ‘onReceiveLog’ interface may be duplicated. You can perform deduplication based on the ‘blockNumber, transactionIndex, and logIndex’ in the ‘EventLog’ object
topic Tools¶
‘org.fisco.bcos.sdk.v3.codec.abi.TopicTools’ provides tools for converting various types of parameters into corresponding topics. You can set the ‘topics’ parameter of ‘EventSubParams’ by using the。
class TopicTools {
// int1/uint1~uint1/uint256
public static String integerToTopic(BigInteger i)
// bool
public static String boolToTopic(boolean b)
// address
public static String addressToTopic(String s)
// string
public static String stringToTopic(String s)
// bytes
public static String bytesToTopic(byte[] b)
// byte1~byte32
public static String byteNToTopic(byte[] b)
}
4. Example¶
‘Asset’The ‘TransferEvent’ of the contract is used as an example to illustrate, giving some scenarios of contract event push for users’ reference。
contract Asset {
event TransferEvent(int256 ret, string indexed from_account, string indexed to_account, uint256 indexed amount);
event TransferAccountEvent(string,string);
function transfer(string from_account, string to_account, uint256 amount) public returns(int256) {
/ / Results
int result = 0;
/ / Other logic, omitted
/ / TransferEvent saves the result and interface parameters
TransferEvent(result, from_account, to_account, amount);
/ / Save the account for TransferAccountEvent
TransferAccountEvent(from_account, to_account);
}
}
Scenario 1: All / latest events on the chain are called back to the client
/ / Initialize EventSubscribe
EventSubscribe eventSubscribe = EventSubscribe.build(group, configOption);
eventSubscribe.start();
/ / Parameter settings
EventSubParams params = new EventSubParams();
/ / All Event fromBlock is set to -1
params.setFromBlock(-1);
/ / Set toBlock to -1 and wait for the new block until the latest block is processed
params.setToBlock(-1);
/ / Register event
eventSubscribe.subscribeEvent(params,
(eventSubId, status, logs) -> {
System.out.println("event sub id: " + eventSubId);
System.out.println(" \t status: " + status);
System.out.println(" \t logs: " + logs);
});
Scene 2: callback the latest ‘TransferEvent’ event of the ‘Asset’ contract to the client
/ / Initialize EventSubscribe
EventSubscribe eventSubscribe = EventSubscribe.build(group, configOption);
eventSubscribe.start();
/ / Set parameters
EventSubParams params = new EventSubParams();
/ / Set fromBlock to -1 starting from the latest block at the time of subscription
params.setFromBlock(-1);
/ / Set toBlock to -1 and wait for the new block until the latest block is processed
params.setToBlock(-1);
// topic0,TransferEvent(int256,string,string,uint256)
params.addTopic(0, TopicTools.stringToTopic("TransferEvent(int256,string,string,uint256)");
/ / Register event
eventSubscribe.subscribeEvent(params,
(eventSubId, status, logs) -> {
System.out.println("event sub id: " + eventSubId);
System.out.println(" \t status: " + status);
System.out.println(" \t logs: " + logs);
});
Scene 3: callback the latest ‘TransferEvent’ event of the ‘Asset’ contract at the specified address to the client
Contract Address: String addr = "0x06922a844c542df030a2a2be8f835892db99f324";
/ / Initialize EventSubscribe
EventSubscribe eventSubscribe = EventSubscribe.build(group, configOption);
eventSubscribe.start();
String addr = "0x06922a844c542df030a2a2be8f835892db99f324";
/ / Set parameters
EventSubParams params = new EventSubParams();
/ / Set fromBlock to -1 starting from the latest block at the time of subscription
params.setFromBlock(-1);
/ / Set toBlock to -1 and wait for the new block until the latest block is processed
params.setToBlock(-1);
/ / addresses is set to the asset address, which matches the contract address
params.addAddress("0x06922a844c542df030a2a2be8f835892db99f324");
// topic0,TransferEvent(int256,string,string,uint256)
params.addTopic(0, TopicTools.stringToTopic("TransferEvent(int256,string,string,uint256)");
/ / Register event
eventSubscribe.subscribeEvent(params,
(eventSubId, status, logs) -> {
System.out.println("event sub id: " + eventSubId);
System.out.println(" \t status: " + status);
System.out.println(" \t logs: " + logs);
});
Scene 4: callback all ‘TransferEvent’ events of the ‘Asset’ contract at the specified address to the client
Contract Address: String addr = "0x06922a844c542df030a2a2be8f835892db99f324";
/ / Other initialization logic, omitted
/ / Set parameters
EventSubParams params = new EventSubParams();
/ / From the initial block, fromBlock is set to 1
params.setFromBlock(1);
/ / Set toBlock to -1 and wait for the new block until the latest block is processed
params.setToBlock(-1);
/ / addresses is set to the asset address, which matches the contract address
params.addAddress("0x06922a844c542df030a2a2be8f835892db99f324");
// topic0,TransferEvent(int256,string,string,uint256)
params.addTopic(0, TopicTools.stringToTopic("TransferEvent(int256,string,string,uint256)");
/ / Register event
eventSubscribe.subscribeEvent(params,
(eventSubId, status, logs) -> {
System.out.println("event sub id: " + eventSubId);
System.out.println(" \t status: " + status);
System.out.println(" \t logs: " + logs);
});
4. Parsing Examples¶
The ‘Asset’ contract is used as an example to describe the implementation of contract deployment, invocation, registration of events, and resolution of node push events。Note: The event parameters with the added indexed attribute are not decoded and are recorded directly at the corresponding position. The remaining event parameters with non-indexed attributes will be decoded。
String contractAddress = "";
try {
AssembleTransactionProcessor manager =
TransactionProcessorFactory.createAssembleTransactionProcessor(
client, client.getCryptoSuite().createKeyPair(), abiFile, binFile);
// deploy
TransactionResponse response = manager.deployByContractLoader("Asset", Lists.newArrayList());
if (!response.getTransactionReceipt().isStatusOK()) {
return;
}
contractAddress = response.getContractAddress();
// call function with event
List<Object> paramsSetValues = new ArrayList<Object>();
paramsSetValues.add("Alice");
paramsSetValues.add("Bob");
paramsSetValues.add(new BigInteger("100"));
TransactionResponse transactionResponse =
manager.sendTransactionAndGetResponse(
contractAddress, abi, "transfer", paramsSetValues);
logger.info("transaction response : " + JsonUtils.toJson(transactionResponse));
} catch (Exception e) {
logger.error("exception:", e);
}
// subscribe event
EventSubscribe eventSubscribe = EventSubscribe.build(group, configOption);
eventSubscribe.start();
EventSubParams eventLogParams = new EventSubParams();
eventLogParams.setFromBlock(-1);
eventLogParams.setToBlock(-1);
eventLogParams.addAddress(contractAddress);
CryptoSuite invalidCryptoSuite =
new CryptoSuite(client.getCryptoSuite().getCryptoTypeConfig());
TopicTools topicTools = new TopicTools(invalidCryptoSuite);
eventLogParams.setTopics(topics);
eventLogParams.addTopic(0,topicTools.stringToTopic("TransferEvent(int256,string,string,uint256)"));
eventLogParams.addTopic(0,topicTools.stringToTopic("TransferAccountEvent(string,string)"));
class SubscribeCallback implements EventSubCallback {
public transient Semaphore semaphore = new Semaphore(1, true);
SubscribeCallback() {
try {
semaphore.acquire(1);
} catch (InterruptedException e) {
logger.error("error :", e);
Thread.currentThread().interrupt();
}
}
@Override
public void onReceiveLog(String eventId, int status, List<EventLog> logs) {
String str = "status in onReceiveLog : " + status;
logger.debug(str);
semaphore.release();
// decode event
if (logs != null) {
for (EventLog log : logs) {
logger.debug(
" blockNumber: {}, txIndex:{}, data: {}",
log.getBlockNumber(),
log.getTransactionIndex(),
log.getData());
ContractCodec abiCodec = new ContractCodec(client.getCryptoSuite(), false);
try {
List<Object> list = abiCodec.decodeEvent(abi, "TransferEvent", log);
logger.debug("decode event log content, " + list);
// list = [0,
// 0x81376b9868b292a46a1c486d344e427a3088657fda629b5f4a647822d329cd6a,
// 0x28cac318a86c8a0a6a9156c2dba2c8c2363677ba0514ef616592d81557e679b6,
// 0x0000000000000000000000000000000000000000000000000000000000000064]
/ / The last three event parameters are indexed
} catch (ContractCodecException e) {
logger.error("decode event log error, " + e.getMessage());
}
try {
List<Object> list =
abiCodec.decodeEvent(abi, "TransferAccountEvent", log);
logger.debug("decode event log content, " + list);
// list = [Alice, Bob]
} catch (ContractCodecException e) {
logger.error("decode event log error, " + e.getMessage());
}
}
}
}
}
SubscribeCallback subscribeEventCallback1 = new SubscribeCallback();
String registerId =
eventSubscribe.subscribeEvent(eventLogParams, subscribeEventCallback1);
try {
subscribeEventCallback1.semaphore.acquire(1);
subscribeEventCallback1.semaphore.release();
logger.info("subscribe successful, registerId is " + registerId);
} catch (InterruptedException e) {
logger.error("system error:", e);
Thread.currentThread().interrupt();
}