1<!DOCTYPE html> 2<html> 3 <head> 4 <title>Deep Dive into Ethereum Logs â Hongchao's Notes â sensing the abstract nonsense</title> 5 6 <meta charset="utf-8" /> 7 <meta content='text/html; charset=utf-8' http-equiv='Content-Type'> 8 <meta http-equiv='X-UA-Compatible' content='IE=edge'> 9 <meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0'> 10 11 12 <meta name="description" content="Logs in Ethereum are very useful since they are the underlying 13mechanism for events, which is the recommended way for smart contracts 14to communicate with the outside world. They also provide a 15significantly cheaper way to store data on-chain. 16 17 18 19 20" /> 21 <meta property="og:description" content="Logs in Ethereum are very useful since they are the underlying 22mechanism for events, which is the recommended way for smart contracts 23to communicate with the outside world. They also provide a 24significantly cheaper way to store data on-chain. 25 26 27 28 29" /> 30 31 <meta name="author" content="Hongchao's Notes" /> 32 33 34 <meta property="og:title" content="Deep Dive into Ethereum Logs" /> 35 <meta property="twitter:title" content="Deep Dive into Ethereum Logs" /> 36 37 38 <!--[if lt IE 9]> 39
39<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
39 40 <![endif]--> 41 42 <link rel="stylesheet" type="text/css" href="/style.css" /> 43 <link rel="alternate" type="application/rss+xml" title="Hongchao's Notes - sensing the abstract nonsense" href="/feed.xml" /> 44 45 <!-- Created with Jekyll Now - http://github.com/barryclark/jekyll-now --> 46 </head> 47 48 <body> 49 <div class="wrapper-masthead"> 50 <div class="container"> 51 <header class="masthead clearfix"> 52 <a href="/" class="site-avatar"><img src="https://hongchao.me/images/logo.jpg" /></a> 53 54 <div class="site-info"> 55 <h1 class="site-name"><a href="/"><span class="hongchao">Hongchao's</span> <span class="notes">Notes</span></a></h1> 56 <p class="site-description">sensing the abstract nonsense</p> 57 <p class="fingerprint">E55D 0DFC 2B7D 7B95</p> 58 </div> 59 60 <nav> 61 <a href="/about">About</a> 62 <a href="https://alephium.org">Alephium</a> 63 <a href="https://bitcoin-decoder.softfork.se">Nioctib</a> 64 </nav> 65 </header> 66 </div> 67 </div> 68 69 <div id="main" role="main" class="container"> 70 <article class="post"> 71 <h1>Deep Dive into Ethereum Logs</h1> 72 73 <div class="entry"> 74 <p>Logs in Ethereum are very useful since they are the underlying 75mechanism for events, which is the recommended way for smart contracts 76to communicate with the outside world. They also provide a 77significantly cheaper way to store data on-chain. 78<br /> 79<br /> 80<img src="/images/logs.png" alt="logs" style="width: 360px;" /></p> 81 82<p>As an example, the following code snippet from the 83<a href="https://github.com/Uniswap/v2-core/blob/4dd59067c76dea4a0e8e4bfdda41877a6b16dedc/contracts/UniswapV2ERC20.sol#L60">UniswapV2ERC20.sol</a> 84contract emits a 85<a href="https://github.com/Uniswap/v2-core/blob/4dd59067c76dea4a0e8e4bfdda41877a6b16dedc/contracts/UniswapV2ERC20.sol#L22">Transfer</a> 86event whenever a 87<a href="https://github.com/Uniswap/v2-core/blob/4dd59067c76dea4a0e8e4bfdda41877a6b16dedc/contracts/UniswapV2ERC20.sol#L68">transfer</a> 88of tokens takes place.</p> 89 90<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">event</span> <span class="nx">Transfer</span><span class="p">(</span><span class="nx">address</span> <span class="nx">indexed</span> <span class="k">from</span><span class="p">,</span> <span class="nx">address</span> <span class="nx">indexed</span> <span class="nx">to</span><span class="p">,</span> <span class="nx">uint</span> <span class="nx">value</span><span class="p">);</span> 91 92<span class="kd">function</span> <span class="nx">_transfer</span><span class="p">(</span><span class="nx">address</span> <span class="k">from</span><span class="p">,</span> <span class="nx">address</span> <span class="nx">to</span><span class="p">,</span> <span class="nx">uint</span> <span class="nx">value</span><span class="p">)</span> <span class="kr">private</span> <span class="p">{</span> 93 <span class="nx">balanceOf</span><span class="p">[</span><span class="k">from</span><span class="p">]</span> <span class="o">=</span> <span class="nx">balanceOf</span><span class="p">[</span><span class="k">from</span><span class="p">].</span><span class="nx">sub</span><span class="p">(</span><span class="nx">value</span><span class="p">);</span> 94 <span class="nx">balanceOf</span><span class="p">[</span><span class="nx">to</span><span class="p">]</span> <span class="o">=</span> <span class="nx">balanceOf</span><span class="p">[</span><span class="nx">to</span><span class="p">].</span><span class="nx">add</span><span class="p">(</span><span class="nx">value</span><span class="p">);</span> 95 <span class="nx">emit</span> <span class="nx">Transfer</span><span class="p">(</span><span class="k">from</span><span class="p">,</span> <span class="nx">to</span><span class="p">,</span> <span class="nx">value</span><span class="p">);</span> 96<span class="p">}</span></code></pre></figure> 97 98<p>For a more detailed description of the Ethereum logs, including the 99concept of indexes/topics, data and cost, etc, highly recommend Luit 100Hollanderâs excellent blog post <a href="https://medium.com/mycrypto/understanding-event-logs-on-the-ethereum-blockchain-f4ae7ba
10050378">Understanding event logs on the 101Ethereum 102blockchain</a>. This 103blog post will instead focus on how logs are actually implemented in Ethereum, 104specifically in <a href="https://geth.ethereum.org/">Geth</a>.</p> 105 106<p>When an event is emitted from a smart contract, one of the <a href="https://github.com/ethereum/go-ethereum/blob/b1e72f7ea998ad662166bcf23705ca59cf81e925/core/vm/opcodes.go#L198">LOG 107opcodes</a> 108will be generated by the smart contract compiler. Events can have 109maximum 4 topics/indexes, which is why there are 5 LOG opcodes (LOG0, 110LOG1, â¦, LOG4) defined. The 111<a href="https://github.com/Uniswap/v2-core/blob/4dd59067c76dea4a0e8e4bfdda41877a6b16dedc/contracts/UniswapV2ERC20.sol#L22">Transfer</a> 112event in the above example has three indexes/topics in total: two 113explicit indexes <strong>from</strong> and <strong>to</strong>, and one implicit index which is 114the hash of the event signature (see Luitâs 115<a href="https://medium.com/mycrypto/understanding-event-logs-on-the-ethereum-blockchain-f4ae7ba50378">post</a>), 116in this case <strong>Transfer(address, address, uint)</strong>. As the result, the 117generated opscode is 118<a href="https://github.com/ethereum/go-ethereum/blob/b1e72f7ea998ad662166bcf23705ca59cf81e925/core/vm/opcodes.go#L202">LOG3</a>.</p> 119 120<p>LOG opcode is then interpreted by the <a href="https://github.com/ethereum/go-ethereum/blob/master/core/vm/instructions.go#L844">makeLog(size 121int)</a> 122function, which basically reads the indexes as well as a blob of 123arbitrary data from the stack, uses them to construct a 124<a href="https://github.com/ethereum/go-ethereum/blob/master/core/types/log.go#L31">log</a> 125object and then adds it to 126<a href="https://github.com/ethereum/go-ethereum/blob/master/core/state/statedb.go#L64">StateDB</a>. <strong>StateDB</strong> 127is an EVM data structure that stores anything within the merkle trie, and 128is the general query interface to retrieve contracts and 129accounts. Itâs used to store data during transaction execution, 130including logs.</p> 131 132<figure class="highlight"><pre><code class="language-go" data-lang="go"><span class="k">func</span> <span class="n">makeLog</span><span class="p">(</span><span class="n">size</span> <span class="kt">int</span><span class="p">)</span> <span class="n">executionFunc</span> <span class="p">{</span> 133 <span class="k">return</span> <span class="k">func</span><span class="p">(</span><span class="n">pc</span> <span class="o">*</span><span class="kt">uint64</span><span class="p">,</span> <span class="n">interpreter</span> <span class="o">*</span><span class="n">EVMInterpreter</span><span class="p">,</span> <span class="n">scope</span> <span class="o">*</span><span class="n">ScopeContext</span><span class="p">)</span> <span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span> 134 <span class="c">// ... skip ...</span> 135 <span class="k">for</span> <span class="n">i</span> <span class="o">:=</span> <span class="m">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">size</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span> <span class="p">{</span> 136 <span class="n">addr</span> <span class="o">:=</span> <span class="n">stack</span><span class="o">.</span><span class="n">pop</span><span class="p">()</span> 137 <span class="n">topics</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">addr</span><span class="o">.</span><span class="n">Bytes32</span><span class="p">()</span> 138 <span class="p">}</span> 139 140 <span class="c">// ... skip ...</span> 141 <span class="n">interpreter</span><span class="o">.</span><span class="n">evm</span><span class="o">.</span><span class="n">StateDB</span><span class="o">.</span><span class="n">AddLog</span><span class="p">(</span><span class="o">&</span><span class="n">types</span><span class="o">.</span><span class="n">Log</span><span class="p">{</span> 142 <span class="n">Address</span><span class="o">:</span> <span class="n">scope</span><span class="o">.</span><span class="n">Contract</span><span class="o">.</span><span class="n">Address</span><span class="p">(),</span> 143 <span class="n">Topics</span><span class="o">:</span> <span class="n">topics</span><span class="p">,</span> 144 <span class="n">Data</span><span class="o">:</span> <span class="n">d</span><span class="p">,</span> 145 <span class="c">// This is a non-consensus field, but assigned here because</span> 146 <span class="c">// core/state doesn't know the current block number.</span> 147 <span class="n">BlockNumber</span><span class="o">:</span> <span class="n">interpreter</span><span class="o">.</span><span class="n">evm</span><span class="o">.</span><span class="n">Context</span><span class="o">.</span><span class="n">BlockNumber</span><span class="o">.</span><span class="n">Uint64</span><span class="p">(),</span> 148 <span class="p">})</span>
149 <span class="c">// ... skip ...</span> 150 <span class="p">}</span> 151<span class="p">}</span></code></pre></figure> 152 153<p>When a transaction is executed using the 154<a href="https://github.com/ethereum/go-ethereum/blob/master/core/state_processor.go#L95">applyTransaction</a> 155function, the generated <a href="http://man.hubwiz.com/docset/Ethereum.docset/Contents/Resources/Documents/eth_getTransactionReceipt.html">transaction 156receipt</a> 157contains logs that are retreived from <strong>StateDB</strong> together with a 158bloom filter that statistically keeps track of all the topics of the 159log as well as the address of the contract that emits the logs. The 160reason to use a <a href="https://en.wikipedia.org/wiki/Bloom_filter">bloom 161filter</a> here is to strike 162a balance between query efficiency and storage space. If a user wants 163to search for logs with a set of topics, these topics can be run 164against the bloom filter to probabilistically determine if any log 165match the search criteria exist in this transaction receipt. False 166positives are removed by checking with the search criteria again.</p> 167 168<figure class="highlight"><pre><code class="language-go" data-lang="go"><span class="k">func</span> <span class="n">applyTransaction</span><span class="p">(</span><span class="n">msg</span> <span class="n">types</span><span class="o">.</span><span class="n">Message</span><span class="p">,</span> <span class="n">config</span> <span class="o">*</span><span class="n">params</span><span class="o">.</span><span class="n">ChainConfig</span><span class="p">,</span> <span class="n">bc</span> <span class="n">ChainContext</span><span class="p">,</span> <span class="n">author</span> <span class="o">*</span><span class="n">common</span><span class="o">.</span><span class="n">Address</span><span class="p">,</span> <span class="n">gp</span> <span class="o">*</span><span class="n">GasPool</span><span class="p">,</span> <span class="n">statedb</span> <span class="o">*</span><span class="n">state</span><span class="o">.</span><span class="n">StateDB</span><span class="p">,</span> <span class="n">blockNumber</span> <span class="o">*</span><span class="n">big</span><span class="o">.</span><span class="n">Int</span><span class="p">,</span> <span class="n">blockHash</span> <span class="n">common</span><span class="o">.</span><span class="n">Hash</span><span class="p">,</span> <span class="n">tx</span> <span class="o">*</span><span class="n">types</span><span class="o">.</span><span class="n">Transaction</span><span class="p">,</span> <span class="n">usedGas</span> <span class="o">*</span><span class="kt">uint64</span><span class="p">,</span> <span class="n">evm</span> <span class="o">*</span><span class="n">vm</span><span class="o">.</span><span class="n">EVM</span><span class="p">)</span> <span class="p">(</span><span class="o">*</span><span class="n">types</span><span class="o">.</span><span class="n">Receipt</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span> 169 <span class="c">// ... skip ...</span> 170 171 <span class="c">// Set the receipt logs and create the bloom filter.</span> 172 <span class="n">receipt</span><span class="o">.</span><span class="n">Logs</span> <span class="o">=</span> <span class="n">statedb</span><span class="o">.</span><span class="n">GetLogs</span><span class="p">(</span><span class="n">tx</span><span class="o">.</span><span class="n">Hash</span><span class="p">(),</span> <span class="n">blockHash</span><span class="p">)</span> 173 <span class="n">receipt</span><span class="o">.</span><span class="n">Bloom</span> <span class="o">=</span> <span class="n">types</span><span class="o">.</span><span class="n">CreateBloom</span><span class="p">(</span><span class="n">types</span><span class="o">.</span><span class="n">Receipts</span><span class="p">{</span><span class="n">receipt</span><span class="p">})</span> 174 <span class="n">receipt</span><span class="o">.</span><span class="n">BlockHash</span> <span class="o">=</span> <span class="n">blockHash</span> 175 <span class="n">receipt</span><span class="o">.</span><span class="n">BlockNumber</span> <span class="o">=</span> <span class="n">blockNumber</span> 176 177 <span class="c">
177// ... skip ...</span> 178<span class="p">}</span></code></pre></figure> 179 180<p>When a block is created in the 181<a href="https://github.com/ethereum/go-ethereum/blob/master/core/types/block.go#L198">NewBlock</a> 182function, it creates a bloom filter in the block header which 183essentially combines the bloom filters of all the transaction receipts 184included in this block. This allows quick check of whether any logs 185with matching criteria exists in this block.</p> 186 187<figure class="highlight"><pre><code class="language-go" data-lang="go"><span class="k">func</span> <span class="n">NewBlock</span><span class="p">(</span><span class="n">header</span> <span class="o">*</span><span class="n">Header</span><span class="p">,</span> <span class="n">txs</span> <span class="p">[]</span><span class="o">*</span><span class="n">Transaction</span><span class="p">,</span> <span class="n">uncles</span> <span class="p">[]</span><span class="o">*</span><span class="n">Header</span><span class="p">,</span> <span class="n">receipts</span> <span class="p">[]</span><span class="o">*</span><span class="n">Receipt</span><span class="p">,</span> <span class="n">hasher</span> <span class="n">TrieHasher</span><span class="p">)</span> <span class="o">*</span><span class="n">Block</span> <span class="p">{</span> 188 <span class="c">// ... skip ...</span> 189 190 <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">receipts</span><span class="p">)</span> <span class="o">==</span> <span class="m">0</span> <span class="p">{</span> 191 <span class="n">b</span><span class="o">.</span><span class="n">header</span><span class="o">.</span><span class="n">ReceiptHash</span> <span class="o">=</span> <span class="n">EmptyRootHash</span> 192 <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> 193 <span class="n">b</span><span class="o">.</span><span class="n">header</span><span class="o">.</span><span class="n">ReceiptHash</span> <span class="o">=</span> <span class="n">DeriveSha</span><span class="p">(</span><span class="n">Receipts</span><span class="p">(</span><span class="n">receipts</span><span class="p">),</span> <span class="n">hasher</span><span class="p">)</span> 194 <span class="n">b</span><span class="o">.</span><span class="n">header</span><span class="o">.</span><span class="n">Bloom</span> <span class="o">=</span> <span class="n">CreateBloom</span><span class="p">(</span><span class="n">receipts</span><span class="p">)</span> 195 <span class="p">}</span> 196 197 <span class="c">// ... skip ...</span> 198<span class="p">}</span></code></pre></figure> 199 200<p>When a block is persisted using 201<a href="https://github.com/ethereum/go-ethereum/blob/master/core/blockchain.go#L1175">writeBlockWithState</a> 202function, it persists all transaction receipts with logs per 203block. The 204<a href="https://github.com/ethereum/go-ethereum/blob/master/core/rawdb/schema.go#L88">format</a> 205of the key value pair is <strong>blockReceiptsPrefix + num (uint64 big endian) + hash -> block receipts</strong>.</p> 206 207<figure class="highlight"><pre><code class="language-go" data-lang="go"><span class="k">func</span> <span class="p">(</span><span class="n">bc</span> <span class="o">*</span><span class="n">BlockChain</span><span class="p">)</span> <span class="n">writeBlockWithState</span><span class="p">(</span><span class="n">block</span> <span class="o">*</span><span class="n">types</span><span class="o">.</span><span class="n">Block</span><span class="p">,</span> <span class="n">receipts</span> <span class="p">[]</span><span class="o">*</span><span class="n">types</span><span class="o">.</span><span class="n">Receipt</span><span class="p">,</span> <span class="n">logs</span> <span class="p">[]</span><span class="o">*</span><span class="n">types</span><span class="o">.</span><span class="n">Log</span><span class="p">,</span> <span class="n">state</span> <span class="o">*</span><span class="n">state</span><span class="o">.</span><span class="n">StateDB</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span> 208 <span class="c">
208// ... skip ...</span> 209 210 <span class="n">rawdb</span><span class="o">.</span><span class="n">WriteBlock</span><span class="p">(</span><span class="n">blockBatch</span><span class="p">,</span> <span class="n">block</span><span class="p">)</span> 211 <span class="n">rawdb</span><span class="o">.</span><span class="n">WriteReceipts</span><span class="p">(</span><span class="n">blockBatch</span><span class="p">,</span> <span class="n">block</span><span class="o">.</span><span class="n">Hash</span><span class="p">(),</span> <span class="n">block</span><span class="o">.</span><span class="n">NumberU64</span><span class="p">(),</span> <span class="n">receipts</span><span class="p">)</span> 212 <span class="n">rawdb</span><span class="o">.</span><span class="n">WritePreimages</span><span class="p">(</span><span class="n">blockBatch</span><span class="p">,</span> <span class="n">state</span><span class="o">.</span><span class="n">Preimages</span><span class="p">())</span> 213 214 <span class="c">// ... skip ...</span> 215<span class="p">}</span></code></pre></figure> 216 217<p>There are a few ways to consume the emmited events. After a 218transaction is mined into the blockchain, 219<a href="https://eth.wiki/json-rpc/API#eth_gettransactionreceipt">eth_getTransactionReceipt</a> 220can be called with the transaction hash to get the <a href="https://eth.wiki/json-rpc/API#eth_getfilterchanges">transaction 221receipt</a>, which 222contains a <code class="language-plaintext highlighter-rouge">logs</code> field that carries an array of log objects that this 223transaction generated. DApp frontends can also leverage the 224combination of the 225<a href="https://eth.wiki/json-rpc/API#eth_newfilter">eth_newFilter</a> and 226<a href="https://eth.wiki/json-rpc/API#eth_getfilterchanges">eth_getFilterChanges</a> 227method to poll the new events of interest. Web3.js abstracts this away 228by providing the 229<a href="https://web3js.readthedocs.io/en/v1.2.11/web3-eth-subscribe.html#subscribe-logs">subscribe(âlogâ)</a> 230method to help real time consumption of the incoming logs using the 231subscription model.</p> 232 233<p>Letâs look at these JSON-RPCs in more details:</p> 234<ul> 235 <li><a href="https://eth.wiki/json-rpc/API#eth_gettransactionreceipt">eth_getTransactionReceipt</a>. 236Return the transaction receipt for a transaction id. Since 237transaction receipts are stored per block, this basically 238<a href="https://github.com/ethereum/go-ethereum/blob/master/internal/ethapi/api.go#L1615">fetches all the receipts for the 239block</a> 240that this transaction belongs to and only return the requested 241one. As mentioned before, logs are part of the transaction receipt.</li> 242 <li><a href="https://eth.wiki/json-rpc/API#eth_newfilter">eth_newFilter</a> & 243<a href="https://eth.wiki/json-rpc/API#eth_getfilterchanges">eth_getFilterChanges</a>. 244Return logs that match the search criteria such as the block range, 245contract address as well as topics. This leverages the bloom filter 246both in the block header as well as in the transaction receipt to 247determine which logs should be returned. False positives are 248filtered again using the search criteria. The filter logic can be found 249<a href="https://github.com/ethereum/go-ethereum/blob/master/eth/filters/filter.go#L119">here</a>. There 250are also some <a href="https://github.com/ethereum/go-ethereum/blob/master/eth/filters/filter.go#L153">index 251optimization</a> 252so that it doesnt have to iterate through each blocks in the block 253range while searching for the matching logs.</li> 254</ul> 255 256<p>Hope this blog post has shed some light on how logging is implemented 257Ethereum. The overall design feels pretty elegant and efficient. One 258tradeoff here is how much data should be indexed on-chain v.s 259off-chain. Ethereum decided to have max 4 topics per log, perhaps a 260good balance between searchability and storage, but one can make an 261legitimate argument that indexes should be pushed more to the 262application layer since data is more understood there and it is easier
263to make changes afterwards.</p> 264 265<p><br /></p> 266 267<hr /> 268 269<p>Reference:</p> 270<ul> 271 <li><a href="https://ethereum.org/ig/developers/tutorials/logging-events-smart-contracts/">Logging data from smart contracts with events</a></li> 272 <li><a href="https://medium.com/mycrypto/understanding-event-logs-on-the-ethereum-blockchain-f4ae7ba50378">Understanding event logs on the Ethereum blockchain</a></li> 273 <li><a href="https://ethereum.stackexchange.com/questions/3418/how-does-ethereum-make-use-of-bloom-filters/3426">How does Ethereum make use of bloom filters?</a></li> 274 <li><a href="https://consensys.net/blog/developers/guide-to-events-and-logs-in-ethereum-smart-contracts">A guide to events and logs in ethereum smart contracts</a></li> 275 <li><a href="https://medium.com/remix-ide/the-anatomy-of-a-transaction-receipt-d935aacc9fcd">The Anatomy of a Transaction Receipt</a></li> 276</ul> 277 278 </div> 279 280 <div class="date"> 281 Written on January 22, 2022 282 </div> 283 284 285<div class="comments"> 286 <div id="disqus_thread"></div> 287
287<script type="text/javascript"> 288 289 var disqus_shortname = 'hcslearningnotes'; 290 291 (function() { 292 var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; 293 dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; 294 (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); 295 })(); 296 297 </script>
297 298 <noscript>Please enable JavaScript to view the <a href="http://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript> 299</div> 300 301</article> 302 303 </div> 304 305 <div class="wrapper-footer"> 306 <div class="container"> 307 <footer class="footer"> 308 309 310 311 312<a href="https://github.com/h0ngcha0"><i class="svg-icon github"></i></a> 313 314<a href="https://www.linkedin.com/in/liuhongchao"><i class="svg-icon linkedin"></i></a> 315 316<a href="/feed.xml"><i class="svg-icon rss"></i></a> 317<a href="https://www.twitter.com/hongchao"><i class="svg-icon twitter"></i></a> 318 319 320 321 322 </footer> 323 </div> 324 </div> 325 326 327 <!-- Google Analytics --> 328
328<script>
vendor: 339 bytes, lines 328-334
328 329 (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ 330 (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), 331 m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) 332 })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); 333 334 ga('create', '
334UA-54771594-1
vendor: 14 bytes, lines 334-335
334', 'auto'); 335
335ga('send', 'pageview', { 336 'page': '/eth-event-logs/', 337 'title': 'Deep Dive into Ethereum Logs' 338 }); 339 </script>
339 340 <!-- End Google Analytics --> 341 342 343 </body> 344</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.