In a few posts building the Backend for the Decentralized Application (ĐApp) we also mentioned that the event in Ethereum is unstable. So we need to build a mechanism that can listen, store and handle events more effectively.
Eventeum is a solution.
What is the eventeum
The eventeum is an Ethereum event listener , acting as a bridge between the smart contract event and the backend.
Whenever an event occurs, a message containing all the details of the event will be stored at the message bus
(Kafka or RabbitMQ), then processed by backend services.
The eventeum is open source , developed by the kauri.io team.
Why Eventeum
If compared to listening directly and handling event directly in the backend, Eventeum has many outstanding points, such as:
- Dynamically Configurable : The eventeum provides REST API to make subscribed / unsubscribed to any event very simple.
- Highly Available : The Eventeum always ensures that all instances will subscribed to the same event collection with each smart contract.
- Resillent : Although the node we connect to may die, the event subscription will continue once the node is alive again.
- Fork Tollerance : The eventeum can be configured to wait for a period of time after a certain number of blocks have been
confirmed
. If afork
event occurs during that period, a message will be broadcast to the entire network, making it possible to handle the logic specific to this fork event.
Deploying Eventeum
Eventeum supports the following broadcast message methods:
- Kafka
- HTTP Post
- RabbitMQ
- Pulsar
With RabbitMQ we can configure the following fields:
rabbitmq.blockNotification
(true or false)rabbitmq.routingKey.contractEvents
rabbitmq.routingKey.blockEvents
rabbitmq.routingKey.transactionEvents
Prerequisites
- Java8
- Maven
- Mongo
- Kafka
- Zookeeper
- Ethereum Node
- Docker
It may seem like a lot of hassle when a lot of things are installed, but we can simplify this installation by using Docker as follows:
- clone repo of Eventeum about:
1 2 | <span class="token function">git</span> clone https://github.com/ConsenSys/eventeum.git |
- Move to the root directory
1 2 | <span class="token function">cd</span> eventeum |
- compile and test package
1 2 | mvn clean package |
- run docker
1 2 3 4 | <span class="token function">cd</span> server docker-compose -f docker-compose.yml build docker-compose -f docker-compose.yml up |
Ok so we have the Eventeum running on our machine. The eventeum will default to running at port 8060
.
Deploy smart contract
In this article we will use the Remix IDE to write and deploy contracts quickly.
The blockchain used is ganache, run by ganache-cli
at port 8545. If you don’t have ganache-cli, you can install it using npm
as follows:
1 2 | <span class="token function">npm</span> <span class="token function">install</span> -g ganache-cli |
and proceed to run the chain up:
1 2 | ganache-cli |
Now we have a blockchain running at http://127.0.0.1:8545
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | Gas Price <span class="token operator">===</span> <span class="token operator">===</span> <span class="token operator">===</span> <span class="token operator">===</span> <span class="token operator">===</span> <span class="token operator">===</span> <span class="token number">20000000000</span> Gas Limit <span class="token operator">===</span> <span class="token operator">===</span> <span class="token operator">===</span> <span class="token operator">===</span> <span class="token operator">===</span> <span class="token operator">===</span> <span class="token number">6721975</span> Call Gas Limit <span class="token operator">===</span> <span class="token operator">===</span> <span class="token operator">===</span> <span class="token operator">===</span> <span class="token operator">===</span> <span class="token operator">===</span> <span class="token number">9007199254740991</span> Listening on <span class="token number">127.0</span> <span class="token number">.0</span> <span class="token number">.1</span> <span class="token punctuation">:</span> <span class="token number">8545</span> |
Next, we will prepare a simple contract as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | pragma solidity <span class="token operator">^</span> <span class="token number">0.5</span> <span class="token number">.0</span> <span class="token punctuation">;</span> contract Counter <span class="token punctuation">{</span> uint256 counter <span class="token punctuation">;</span> <span class="token keyword">function</span> <span class="token keyword">get</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> external view <span class="token function">returns</span> <span class="token punctuation">(</span> uint256 <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> counter <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">function</span> <span class="token keyword">set</span> <span class="token punctuation">(</span> uint256 newValue <span class="token punctuation">)</span> external <span class="token punctuation">{</span> counter <span class="token operator">=</span> newValue <span class="token punctuation">;</span> emit <span class="token function">CounterUpdated</span> <span class="token punctuation">(</span> newValue <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> event <span class="token function">CounterUpdated</span> <span class="token punctuation">(</span> uint256 newCounter <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
On Remix, proceed with the compile contract, make sure that no errors occur:
Before deploying, remember to select the network as Web3 Provider
and select the address http://127.0.0.1:8545
as the blockchain address we just ran above.
and proceed to Deploy:
So now we have the contract deployed to the blockchain.
Register Event
Next we will register the event with the Eventeum, so that whenever the event occurs it will capture that event, here is the CounterUpdated
event we have defined in the contract.
- URL:
/api/rest/v1/event-filter
- Method:
POST
For simplicity, here we call with curl, replace the CONTRACT_ADDRESS
segment with the contract address we deployed at Remix above:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <span class="token function">curl</span> -X POST http://localhost:8060/api/rest/v1/event-filter -H <span class="token string">'Cache-Control: no-cache'</span> -H <span class="token string">'Content-Type: application/json'</span> -H <span class="token string">'Postman-Token: 616712a3-bf11-bbf5-b4ac-b82835779d51'</span> -d <span class="token string">'{ "id": "CounterUpdatedEvent", "contractAddress": "CONTRACT_ADDRESS", "eventSpecification": { "eventName": "CounterUpdated", "nonIndexedParameterDefinitions": [{"position": 0, "type": "UINT256"}] } }'</span> |
If successful, we will see a message like this in the docker log mesages:
1 2 | registerContractEventFilter <span class="token operator">-</span> Registered filters <span class="token punctuation">:</span> <span class="token punctuation">{</span> <span class="token string">"CounterUpdatedEvent"</span> <span class="token punctuation">:</span> <span class="token punctuation">{</span> <span class="token string">"filter"</span> <span class="token punctuation">:</span> <span class="token punctuation">{</span> <span class="token string">"id"</span> <span class="token punctuation">:</span> <span class="token string">"CounterUpdatedEvent"</span> <span class="token punctuation">,</span> <span class="token string">"contractAddress"</span> <span class="token punctuation">:</span> <span class="token string">"0x1F5DAf1B8aE9fE2C28Bb8206dF13962906A98db0"</span> <span class="token punctuation">,</span> <span class="token string">"node"</span> <span class="token punctuation">:</span> <span class="token string">"default"</span> <span class="token punctuation">,</span> <span class="token string">"eventSpecification"</span> <span class="token punctuation">:</span> <span class="token punctuation">{</span> <span class="token string">"eventName"</span> <span class="token punctuation">:</span> <span class="token string">"CounterUpdated"</span> <span class="token punctuation">,</span> <span class="token string">"indexedParameterDefinitions"</span> <span class="token punctuation">:</span> <span class="token punctuation">[</span> <span class="token punctuation">]</span> <span class="token punctuation">,</span> <span class="token string">"nonIndexedParameterDefinitions"</span> <span class="token punctuation">:</span> <span class="token punctuation">[</span> <span class="token punctuation">{</span> <span class="token string">"position"</span> <span class="token punctuation">:</span> <span class="token number">0</span> <span class="token punctuation">,</span> <span class="token string">"type"</span> <span class="token punctuation">:</span> <span class="token string">"UINT256"</span> <span class="token punctuation">}</span> <span class="token punctuation">]</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">,</span> <span class="token string">"subscription"</span> <span class="token punctuation">:</span> BufferAsyncEmitter <span class="token punctuation">{</span> <span class="token number">9223372036854775807</span> <span class="token punctuation">}</span> <span class="token punctuation">,</span> <span class="token string">"startBlock"</span> <span class="token punctuation">:</span> <span class="token number">0</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
We will check if after registering with the Eventeum then when interacting with the contract, the event was fired by going to Remix and performing the following transaction:
On the docker logs of the eventeum, we get the following log:
1 2 | broadcastContractEvent <span class="token operator">-</span> Sending contract event message <span class="token punctuation">:</span> <span class="token punctuation">{</span> <span class="token string">"id"</span> <span class="token punctuation">:</span> <span class="token string">"0x00d319cdc9cc80de18642f7f2bb1e3701433f2417225d61791266e055fe07541-0x3f2b9dfc3e9dc86aea43dd11ec6482dfdc05cc454c9de81b19e3b5344515e69a-0"</span> <span class="token punctuation">,</span> <span class="token string">"type"</span> <span class="token punctuation">:</span> <span class="token string">"CONTRACT_EVENT"</span> <span class="token punctuation">,</span> <span class="token string">"details"</span> <span class="token punctuation">:</span> <span class="token punctuation">{</span> <span class="token string">"name"</span> <span class="token punctuation">:</span> <span class="token string">"CounterUpdated"</span> <span class="token punctuation">,</span> <span class="token string">"filterId"</span> <span class="token punctuation">:</span> <span class="token string">"CounterUpdatedEvent"</span> <span class="token punctuation">,</span> <span class="token string">"nodeName"</span> <span class="token punctuation">:</span> <span class="token string">"default"</span> <span class="token punctuation">,</span> <span class="token string">"indexedParameters"</span> <span class="token punctuation">:</span> <span class="token punctuation">[</span> <span class="token punctuation">]</span> <span class="token punctuation">,</span> <span class="token string">"nonIndexedParameters"</span> <span class="token punctuation">:</span> <span class="token punctuation">[</span> <span class="token punctuation">{</span> <span class="token string">"type"</span> <span class="token punctuation">:</span> <span class="token string">"uint256"</span> <span class="token punctuation">,</span> <span class="token string">"value"</span> <span class="token punctuation">:</span> <span class="token number">123</span> <span class="token punctuation">}</span> <span class="token punctuation">]</span> <span class="token punctuation">,</span> <span class="token string">"transactionHash"</span> <span class="token punctuation">:</span> <span class="token string">"0x00d319cdc9cc80de18642f7f2bb1e3701433f2417225d61791266e055fe07541"</span> <span class="token punctuation">,</span> <span class="token string">"logIndex"</span> <span class="token punctuation">:</span> <span class="token number">0</span> <span class="token punctuation">,</span> <span class="token string">"blockNumber"</span> <span class="token punctuation">:</span> <span class="token number">12</span> <span class="token punctuation">,</span> <span class="token string">"blockHash"</span> <span class="token punctuation">:</span> <span class="token string">"0x3f2b9dfc3e9dc86aea43dd11ec6482dfdc05cc454c9de81b19e3b5344515e69a"</span> <span class="token punctuation">,</span> <span class="token string">"address"</span> <span class="token punctuation">:</span> <span class="token string">"0x492934308E98b590A626666B703A6dDf2120e85e"</span> <span class="token punctuation">,</span> <span class="token string">"status"</span> <span class="token punctuation">:</span> <span class="token string">"UNCONFIRMED"</span> <span class="token punctuation">,</span> <span class="token string">"eventSpecificationSignature"</span> <span class="token punctuation">:</span> <span class="token string">"0x4785d80d2593e2cb7a3331d31eb5106408bdde2aab0db9e9b616b036a1b6039d"</span> <span class="token punctuation">,</span> <span class="token string">"networkName"</span> <span class="token punctuation">:</span> <span class="token string">"default"</span> <span class="token punctuation">,</span> <span class="token string">"id"</span> <span class="token punctuation">:</span> <span class="token string">"0x00d319cdc9cc80de18642f7f2bb1e3701433f2417225d61791266e055fe07541-0x3f2b9dfc3e9dc86aea43dd11ec6482dfdc05cc454c9de81b19e3b5344515e69a-0"</span> <span class="token punctuation">}</span> <span class="token punctuation">,</span> <span class="token string">"retries"</span> <span class="token punctuation">:</span> <span class="token number">0</span> <span class="token punctuation">}</span> |
means that our event listener has been successfully installed!
Eventeum has many other APIs, you can refer to here .
Subscribing Eventeum events in the NodeJs application
We will build a small application nodejs to be able to visualize how the Eventeum integration into the app is realistic.
1 2 3 4 5 6 7 | <span class="token function">mkdir</span> watcher <span class="token function">cd</span> watcher <span class="token function">npm</span> init <span class="token function">npm</span> <span class="token function">install</span> <span class="token function">npm</span> i kafka-node <span class="token punctuation">(</span> Kafka-nodejs client <span class="token punctuation">)</span> <span class="token function">touch</span> index.js |
The content of index.js
very simple, listen to what you can log out. Here we use topic contract-events
, this is a builtin of Eventeum, it will check all the topics that have been defined inside the Eventeum.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <span class="token keyword">var</span> kafka <span class="token operator">=</span> <span class="token function">require</span> <span class="token punctuation">(</span> <span class="token string">'kafka-node'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> client <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">kafka <span class="token punctuation">.</span> KafkaClient</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> kafkaHost <span class="token punctuation">:</span> <span class="token string">'localhost:9092'</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">var</span> kafka <span class="token operator">=</span> <span class="token function">require</span> <span class="token punctuation">(</span> <span class="token string">'kafka-node'</span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> Consumer <span class="token operator">=</span> kafka <span class="token punctuation">.</span> Consumer <span class="token punctuation">,</span> consumer <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Consumer</span> <span class="token punctuation">(</span> client <span class="token punctuation">,</span> <span class="token punctuation">[</span> <span class="token punctuation">{</span> topic <span class="token punctuation">:</span> <span class="token string">'contract-events'</span> <span class="token punctuation">,</span> partition <span class="token punctuation">:</span> <span class="token number">0</span> <span class="token punctuation">}</span> <span class="token punctuation">]</span> <span class="token punctuation">,</span> <span class="token punctuation">{</span> autoCommit <span class="token punctuation">:</span> <span class="token boolean">false</span> <span class="token punctuation">,</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> consumer <span class="token punctuation">.</span> <span class="token function">on</span> <span class="token punctuation">(</span> <span class="token string">'message'</span> <span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span> message <span class="token punctuation">)</span> <span class="token punctuation">{</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> message <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
Run another transaction on Remix:
We will see that the nodejs log will look like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <span class="token punctuation">{</span> topic <span class="token punctuation">:</span> <span class="token string">'contract-events'</span> <span class="token punctuation">,</span> value <span class="token punctuation">:</span> <span class="token string">'{"id":"0xdf064648f5923f3f015f53d464f2d95fce2540b0ab6a1e851e3603ff781b894b-0x204c9b64582e5154fa63f3e32c9b3960ea30a88c6d1f97abe7985a16b2762805-0","type":"CONTRACT_EVENT","details":{"name":"CounterUpdated","filterId":"CounterUpdatedEvent","nodeName":"default","indexedParameters":[],"nonIndexedParameters":[{"type":"uint256","value":123}],"transactionHash":"0xdf064648f5923f3f015f53d464f2d95fce2540b0ab6a1e851e3603ff781b894b","logIndex":0,"blockNumber":11,"blockHash":"0x204c9b64582e5154fa63f3e32c9b3960ea30a88c6d1f97abe7985a16b2762805","address":"0x492934308E98b590A626666B703A6dDf2120e85e","status":"UNCONFIRMED","eventSpecificationSignature":"0x4785d80d2593e2cb7a3331d31eb5106408bdde2aab0db9e9b616b036a1b6039d","networkName":"default","id":"0xdf064648f5923f3f015f53d464f2d95fce2540b0ab6a1e851e3603ff781b894b-0x204c9b64582e5154fa63f3e32c9b3960ea30a88c6d1f97abe7985a16b2762805-0"},"retries":0}'</span> <span class="token punctuation">,</span> offset <span class="token punctuation">:</span> <span class="token number">0</span> <span class="token punctuation">,</span> partition <span class="token punctuation">:</span> <span class="token number">0</span> <span class="token punctuation">,</span> highWaterOffset <span class="token punctuation">:</span> <span class="token number">2</span> <span class="token punctuation">,</span> key <span class="token punctuation">:</span> <span class="token string">'0xdf064648f5923f3f015f53d464f2d95fce2540b0ab6a1e851e3603ff781b894b-0x204c9b64582e5154fa63f3e32c9b3960ea30a88c6d1f97abe7985a16b2762805-0'</span> <span class="token punctuation">}</span> <span class="token punctuation">{</span> topic <span class="token punctuation">:</span> <span class="token string">'contract-events'</span> <span class="token punctuation">,</span> value <span class="token punctuation">:</span> <span class="token string">'{"id":"0x00d319cdc9cc80de18642f7f2bb1e3701433f2417225d61791266e055fe07541-0x3f2b9dfc3e9dc86aea43dd11ec6482dfdc05cc454c9de81b19e3b5344515e69a-0","type":"CONTRACT_EVENT","details":{"name":"CounterUpdated","filterId":"CounterUpdatedEvent","nodeName":"default","indexedParameters":[],"nonIndexedParameters":[{"type":"uint256","value":234567}],"transactionHash":"0x00d319cdc9cc80de18642f7f2bb1e3701433f2417225d61791266e055fe07541","logIndex":0,"blockNumber":12,"blockHash":"0x3f2b9dfc3e9dc86aea43dd11ec6482dfdc05cc454c9de81b19e3b5344515e69a","address":"0x492934308E98b590A626666B703A6dDf2120e85e","status":"UNCONFIRMED","eventSpecificationSignature":"0x4785d80d2593e2cb7a3331d31eb5106408bdde2aab0db9e9b616b036a1b6039d","networkName":"default","id":"0x00d319cdc9cc80de18642f7f2bb1e3701433f2417225d61791266e055fe07541-0x3f2b9dfc3e9dc86aea43dd11ec6482dfdc05cc454c9de81b19e3b5344515e69a-0"},"retries":0}'</span> <span class="token punctuation">,</span> offset <span class="token punctuation">:</span> <span class="token number">1</span> <span class="token punctuation">,</span> partition <span class="token punctuation">:</span> <span class="token number">0</span> <span class="token punctuation">,</span> highWaterOffset <span class="token punctuation">:</span> <span class="token number">2</span> <span class="token punctuation">,</span> key <span class="token punctuation">:</span> <span class="token string">'0x00d319cdc9cc80de18642f7f2bb1e3701433f2417225d61791266e055fe07541-0x3f2b9dfc3e9dc86aea43dd11ec6482dfdc05cc454c9de81b19e3b5344515e69a-0'</span> <span class="token punctuation">}</span> |
thus we have successfully integrated Eventume into a nodejs application.
Conclude
With small systems, the event log is not too much, we can use the plan to listen directly from the network and handle it as soon as we can listen.
However as our system grows, requires a lot of events and complex structures, then Eventum will be an effective choice.
Refer
- https://medium.com/quiknode/ethereum-events-monitoring-using-eventeum-f81695d92e05
- https://github.com/ConsenSys/eventeum/
- https://kauri.io/listening-to-ethereum-events-with-eventeum/90dc8d911f1c43008c7d0dfa20bde298/a
- https://kauri.io/listening-for-ethereum-transactions-with-eventeum/3e31587c96a74d24b5cdd17952d983e9/a