OpenRelay

Ethereum Events

## Events - Concept * Stuff happens on the blockchain * Token Transfers * 0x Orders are Filled * Crypto Kitties are Born * Off-chain systems need to know * Process Payments * Prune Order Books * Update search engines * Cheaper than state updates
## Overview * Event Interactions * Solidity (production) * JavaScript (consumption) * Under the hood * Storage * Indexing
## Event Generation * Happens in Contracts * Usually written in Solidity

Solidity Event Definition


							// ERC20 Token Transfer Event
							event Transfer(
							  address indexed from,
							  address indexed to,
							  uint tokens
							);
						
  • Gas:
    375+(8*len(data))+375*len(indexes + 1)
  • Token Transfer Event Gas: 1,756 Gas

Solidity Event Emission


							function transfer(address _to, uint256 _value) public returns (bool) {
							  require(_value <= balances[msg.sender]);
							  require(_to != address(0));

							  balances[msg.sender] = balances[msg.sender].sub(_value);
							  balances[_to] = balances[_to].add(_value);
							  emit Transfer(msg.sender, _to, _value);
							  return true;
							}
						
## Event Processing * Happens Off-chain * Not accessible in contracts * Used to capture changes in off-chain systems * Commonly in JavaScript * OpenRelay does event processing in Go

JavaScript - Event Processing - By Contract


							let contract = web3.eth.contract(abi).at(address);
							let event = contract.Transfer({to: web3.eth.accounts[0]});
							event.watch((err, log) => {
							  console.log("To:", log.args.to);
							  console.log("From:", log.args.from);
							  console.log("Quantity:", log.args.tokens);
							});
							// ...
							event.stopWatching(console.log);
						

JavaScript - Event Processing - Generic


							let watcher = web3.eth.filter({
							  address: contractAddress,
							  topics: [
							    "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
							    "0x0000000000000000000000000000" + web3.eth.accounts[0].slice(2),
							    null,
							  ]
							});
							watcher.watch((err, log) => {
							  console.log("To:", "0x" + log.topics[1].slice(24));
							  console.log("From:", "0x" + log.topics[2].slice(24));
							  console.log("Quantity:", web3.toBigNumber(log.data, "16"));
							});
							// ...
							watcher.stopWatching(console.log);
						
## Under the Hood * Converting Events to Log Messages * Transaction Receipts * Logs * Bloom Filters * Block * Transaction Lists * Bloom Filter
## Log Message Breakdown * `Topic[0]`: keccak256( "Transfer(address,address,uint,)" ) * Does not account for names or indexes * `Topic[N]`: `indexed` field N * `Data`: Byte string, 32 bytes per unindexed field * `Address`: Ethereum address of emitting contract

Transaction Receipt


							{
							  "blockHash": "0xb760a5f2a4a893ab411f0c19697b4347156b6a7acb687867c5b1edc91d20955a",
							  "blockNumber": 6185919,
							  "contractAddress": null,
							  "cumulativeGasUsed": 1441534,
							  "from": "0x74472b403f3591a50dc9bd2ca6154a510d3fe3bf",
							  "gasUsed": 27340,
							  "logs": [
							    {
							      "address": "0x0027449bf0887ca3e431d263ffdefb244d95b555",
							      "topics": [
							        "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
							        "0x00000000000000000000000074472b403f3591a50dc9bd2ca6154a510d3fe3bf",
							        "0x000000000000000000000000c5602be73ab48699ba1067347688cdf5ff57a0d7"
							      ],
							      "data": "0x22ea0179500526edb610f148ec0c614155678491902d5fffffffffffffffffff",
							      "blockNumber": 6185919,
							      "transactionHash": "0x5c13f4909ec63960a218292c408dd99ae87d1be9e6c373df4e81dd168068b363",
							      "transactionIndex": 31,
							      "blockHash": "0xb760a5f2a4a893ab411f0c19697b4347156b6a7acb687867c5b1edc91d20955a",
							      "logIndex": 17,
							      "removed": false
							    }
							  ],
							  "logsBloom": "0x00000100000000000000020000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000008000000000000000000000010200000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000200000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400",
							  "status": "0x1",
							  "to": "0x0027449bf0887ca3e431d263ffdefb244d95b555",
							  "transactionHash": "0x5c13f4909ec63960a218292c408dd99ae87d1be9e6c373df4e81dd168068b363",
							  "transactionIndex": 31
							}
						

Block Header


							{
							  "difficulty": "3477591923752081",
							  "extraData": "0x737061726b706f6f6c2d636e2d6e6f64652d3133",
							  "gasLimit": 7984452,
							  "gasUsed": 7968288,
							  "hash": "0xb760a5f2a4a893ab411f0c19697b4347156b6a7acb687867c5b1edc91d20955a",
							  "logsBloom": "0x09060130909230532809020012002214040414880818401200001108120810848201080020e00481000142d29800000044500c000832409018020211802418210040120014104086a010000c4800048002800d21b214021032c02588080010004008002442620392680c0430004488832c014100040080001441803710820200801e880480622828051a1025008222800100883460204484401c00c0c82900400c009b2040001080100810420032000e0420464400014820810300100040710060425402082a54304504044408850a00250803171000200001039930008164001600013001108400002c22203200102000000024040601010211000000500482",
							  "miner": "0x5a0b54d5dc17e0aadc383d2db43b0a0d3e029c4c",
							  "mixHash": "0xa0d556330d6250c068574e9839127eff248f31555bb093c20a5496f8a47c01ae",
							  "nonce": "0x61ad317c167a530b",
							  "number": 6185919,
							  "parentHash": "0xebaf09f2b1f3445d0a62d33c3a66e62a5a70b056b0442f934c6c2463acd4ee6f",
							  "receiptsRoot": "0x910fe4e945caa8b594e16b432c7d3594412e53acef0fa5fe4cd96c9c8ea1961a",
							  "sha3Uncles": "0x235a56ed5119da8189148c93c641b4e14dff88dd4a4e28e383e582d335dfda60",
							  "size": 15548,
							  "stateRoot": "0x854ebdcde541ccbc556db7d35111189c66b6ca32db84ef8fcc1b8c5884f9c6a1",
							  "timestamp": 1534834069,
							  "totalDifficulty": "6.136611281846183616126e+21",
							  "transactions": [
							    "0x38ce16a7b90b981794aa763ba6d904d52662a12f93b909540ea1905130f95949",
							    "0x63c1eb42dda1c13481550a3ffb894eddd64ea86601ecd0eb19a08e464c46b350",
							    // ...
							    "0x5c13f4909ec63960a218292c408dd99ae87d1be9e6c373df4e81dd168068b363",
							    // ...
							    "0xf7777d7cc6a743d9c6dc0b58aea96c310e7ed0c4173bdb17e032cd4f74979de7",
							    "0xe4ca0cefc4de3574ac109648750fe5e6a6ff7a62fd9f715590eda91803a89d31"
							  ],
							  "transactionsRoot": "0xbfadaf4759f98684dacf1e23c075e990622ce5ac947866f46b8505071db10b03",
							  "uncles": [
							    "0xf3ac3424645df26edd02ee8177779541104fce4360ea7baade05fe23cce78a97",
							    "0x33612fb436ecf17627c4ca65ec4f63bc5e30f442571c813ff9eeee4d1b2c7aa2"
							  ]
							}
						
## Naive Approach for Event Processing * Iterate over every block * Iterate over every transaction * Iterate over every log * Use logs that match filters
## Bloom Filters * Fixed Size Data Structure * Probablistic Membership Testing * Constant Time Lookups * Some false positives * No false negatives

Building a Bloom Filter

H1("dog")

H2("dog")

H3("dog")

BF(["dog"])

Building a Bloom Filter (continued)

H1("cat")

H2("cat")

H3("cat")

BF(["dog", "cat"])

Testing a Bloom Filter

H1("squirrel")

BF(["dog", "cat"])

Testing a Bloom Filter - False Positive

H1("weasel")

H2("weasel")

H3("weasel")

BF(["dog", "cat"])

## Bloom Filter Notes * Error rate determined by: * Number of bits * Number of hash functions * Number of elements in set
## Bloom Filters in Ethereum * 2048 bit bloom filters * 3 hashing functions * Topics & Contract Address added to tx bloom filter * All TX bloom filters added to block bloom filter
## Event Processing with Bloom filters * Iterate over blocks * Check block bloom filter * If block bloom filter matches, iterate over txs * Check tx bloom filter * If tx bloom filter matches, iterate over tx logs * Return matching logs (may be 0)
## Other Event Processing Notes * Clients "Install filters" on RPC Server * Server tracks events against filters as they arrive * Server discards events after serving to client * Clients make recurring "Any new events for this filter?" requests * Filter sessions are stateful - must go to same server

Solidity Event Definition


							// ERC20 Token Transfer Event
							event Transfer(
							  address indexed from,
							  address indexed to,
							  uint tokens
							);
						

Solidity Event Emission


							function transfer(address _to, uint256 _value) public returns (bool) {
							  require(_value <= balances[msg.sender]);
							  require(_to != address(0));

							  balances[msg.sender] = balances[msg.sender].sub(_value);
							  balances[_to] = balances[_to].add(_value);
							  emit Transfer(msg.sender, _to, _value);
							  return true;
							}
						

JavaScript - Event Processing - Generic


							let watcher = web3.eth.filter({
							  address: contractAddress,
							  topics: [
							    "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
							    "0x0000000000000000000000000000" + web3.eth.accounts[0].slice(2),
							    null,
							  ]
							});
							watcher.watch((err, log) => {
							  console.log("To:", "0x" + log.topics[1].slice(24));
							  console.log("From:", "0x" + log.topics[2].slice(24));
							  console.log("Quantity:", web3.toBigNumber(log.data, "16"));
							});
						

JavaScript - Event Processing - By Contract


							let contract = web3.eth.contract(abi).at(address);
							let event = contract.Transfer({to: web3.eth.accounts[0]});
							event.watch((err, log) => {
							  console.log("To:", log.args.to);
							  console.log("From:", log.args.from);
							  console.log("Quantity:", log.args.tokens);
							});
							// ...
							event.stopWatching(console.log);
						
## Next Week * ERC721 Discussion * Open to suggestions