forked from ZcashFoundation/zips
-
Notifications
You must be signed in to change notification settings - Fork 1
/
zip-0208.html
270 lines (264 loc) · 27.6 KB
/
zip-0208.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
<!DOCTYPE html>
<html>
<head>
<title>ZIP 208: Shorter Block Target Spacing</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="css/style.css"></head>
<body>
<section>
<pre>ZIP: 208
Title: Shorter Block Target Spacing
Owner: Daira Hopwood <[email protected]>
Original-Authors: Daira Hopwood
Simon Liu
Status: Final
Category: Consensus
Created: 2019-01-10
License: MIT</pre>
<section id="terminology"><h2><span class="section-heading">Terminology</span><span class="section-anchor"> <a href="#terminology"><img width="24" height="24" src="assets/images/section-anchor.png" alt=""></a></span></h2>
<p>The key words "MUST", "SHOULD", "SHOULD NOT", and "MAY" in this document are to be interpreted as described in RFC 2119. <a id="id1" class="footnote_reference" href="#rfc2119">3</a></p>
<p>The terms "block chain", "consensus rule change", "consensus branch", and "network upgrade" are to be interpreted as defined in <a id="id2" class="footnote_reference" href="#zip-0200">4</a>.</p>
<p>The term "block target spacing" means the time interval between blocks targeted by the difficulty adjustment algorithm in a given consensus branch. It is normally measured in seconds. (This is also sometimes called the "target block time", but "block target spacing" is the term used in <a id="id3" class="footnote_reference" href="#latest-protocol">1</a>.)</p>
</section>
<section id="abstract"><h2><span class="section-heading">Abstract</span><span class="section-anchor"> <a href="#abstract"><img width="24" height="24" src="assets/images/section-anchor.png" alt=""></a></span></h2>
<p>This proposal specifies a change in the block target spacing, to take effect in the Blossom network upgrade <a id="id4" class="footnote_reference" href="#zip-0206">6</a>.</p>
<p>The emission schedule of mined ZEC will be approximately the same in terms of time, but this requires the emission per block to be adjusted to take account of the changed block target spacing.</p>
</section>
<section id="motivation"><h2><span class="section-heading">Motivation</span><span class="section-anchor"> <a href="#motivation"><img width="24" height="24" src="assets/images/section-anchor.png" alt=""></a></span></h2>
<p>The motivations for decreasing the block target spacing are:</p>
<ul>
<li>Reduced latency for considering transactions to be sufficiently confirmed;</li>
<li>Greater throughput of transactions in unit time.</li>
</ul>
<p>The latter goal could alternatively be achieved by increasing the block size limit, but that would not also achieve the former goal.</p>
<p>Note that, for a given security requirement (in terms of the expected cost distribution of a rollback attack), the number of confirmations needed increases more slowly than the decrease in block time, and so, up to a point, decreasing the block target spacing can provide a better trade-off between latency and security. This argument assumes that the verification and propagation time for a block remain small compared to the block target spacing. See <a id="id5" class="footnote_reference" href="#slowfastblocks">8</a> for further analysis in various attack models.</p>
</section>
<section id="specification"><h2><span class="section-heading">Specification</span><span class="section-anchor"> <a href="#specification"><img width="24" height="24" src="assets/images/section-anchor.png" alt=""></a></span></h2>
<p>The changes described in this section are to be made in <a id="id6" class="footnote_reference" href="#latest-protocol">1</a>, relative to the pre-Blossom specification in [#preblossom-protocol].</p>
<section id="consensus-changes"><h3><span class="section-heading">Consensus changes</span><span class="section-anchor"> <a href="#consensus-changes"><img width="24" height="24" src="assets/images/section-anchor.png" alt=""></a></span></h3>
<p>Throughout the specification, rename HalvingInterval to PreBlossomHalvingInterval, and rename PoWTargetSpacing to PreBlossomTargetSpacing. These constants retain their values from <a id="id7" class="footnote_reference" href="#preblossom-protocol">2</a> of 840000 (blocks) and 150 (seconds) respectively.</p>
<p>In section 2 (Notation), add BlossomActivationHeight and PostBlossomPoWTargetSpacing to the list of integer constants.</p>
<p>In section 5.3 (Constants), define PostBlossomPoWTargetSpacing := 75 seconds.</p>
<p>For a given network (production or test), define BlossomActivationHeight as the height at which Blossom activates on that network, as specified in <a id="id8" class="footnote_reference" href="#zip-0206">6</a>.</p>
<p>In section 7.6.3 (Difficulty adjustment), make the following changes:</p>
<p>Define IsBlossomActivated(<em>height</em>) to return true if <em>height</em> ≥ BlossomActivationHeight, otherwise false.</p>
<p>This specification assumes that BlossomActivationHeight ≥ SlowStartInterval.</p>
<p>Define:</p>
<ul>
<li>BlossomPoWTargetSpacingRatio := PreBlossomPoWTargetSpacing / PostBlossomPoWTargetSpacing</li>
<li>PostBlossomHalvingInterval := floor(PreBlossomHalvingInterval · BlossomPoWTargetSpacingRatio).</li>
</ul>
<p>In the same section, redefine PoWTargetSpacing as a function taking a <em>height</em> parameter, as follows:</p>
<ul>
<li>PoWTargetSpacing(<em>height</em>) :=
<ul>
<li>PreBlossomPoWTargetSpacing, if not IsBlossomActivated(<em>height</em>)</li>
<li>PostBlossomPoWTargetSpacing, otherwise.</li>
</ul>
</li>
</ul>
<p>Also redefine AveragingWindowTimespan, MinActualTimespan, MaxActualTimespan, ActualTimespanDamped, ActualTimespanBounded, and Threshold as follows:</p>
<ul>
<li>add a <em>height</em> parameter to each of these functions that does not already have one;</li>
<li>ensure that each reference to any of these values, or to PoWTargetSpacing, are replaced with a function call passing the <em>height</em> parameter.</li>
</ul>
<p>In <a id="id9" class="footnote_reference" href="#latest-protocol">1</a> section 7.7 (Calculation of Block Subsidy and Founders’ Reward), redefine the Halving and BlockSubsidy functions as follows:</p>
<ul>
<li>Halving(<em>height</em>) :=
<ul>
<li>floor((<em>height</em> - SlowStartShift) / PreBlossomHalvingInterval), if not IsBlossomActivated(<em>height</em>)</li>
<li>floor((BlossomActivationHeight - SlowStartShift) / PreBlossomHalvingInterval + (<em>height</em> - BlossomActivationHeight) / PostBlossomHalvingInterval), otherwise</li>
</ul>
</li>
<li>BlockSubsidy(<em>height</em>) :=
<ul>
<li>SlowStartRate · <em>height</em>, if <em>height</em> < SlowStartInterval / 2</li>
<li>SlowStartRate · (<em>height</em> + 1), if SlowStartInterval / 2 ≤ <em>height</em> and <em>height</em> < SlowStartInterval</li>
<li>floor(MaxBlockSubsidy / 2<sup>Halving(*height*)</sup>), if SlowStartInterval ≤ <em>height</em> and not IsBlossomActivated(<em>height</em>)</li>
<li>floor(MaxBlockSubsidy / (BlossomPoWTargetSpacingRatio · 2<sup>Halving(*height*)</sup>)), otherwise</li>
</ul>
</li>
</ul>
<p>Note: BlossomActivationHeight, PostBlossomHalvingInterval, and PostBlossomTargetSpacing are chosen so that:</p>
<ul>
<li>(BlossomActivationHeight - SlowStartShift) / PreBlossomHalvingInterval + (<em>height</em> - BlossomActivationHeight) / PostBlossomHalvingInterval) is exactly 1 for some integer <em>height</em>.</li>
<li>MaxBlockSubsidy / (BlossomPoWTargetSpacingRatio · 2<sup>Halving(*height*)</sup>) is an integer for the next few periods.</li>
</ul>
<p>In <a id="id10" class="footnote_reference" href="#latest-protocol">1</a> section 7.8 (Payment of Founders’ Reward), define:</p>
<ul>
<li>FounderAddressAdjustedHeight(<em>height</em>) :=
<ul>
<li><em>height</em>, if not IsBlossomActivated(<em>height</em>)</li>
<li>BlossomActivationHeight + floor((<em>height</em> - BlossomActivationHeight) / BlossomPoWTargetSpacingRatio), otherwise</li>
</ul>
</li>
</ul>
<p>and in the definition of FounderAddressIndex, replace the use of <em>height</em> with FounderAddressAdjustedHeight(<em>height</em>).</p>
<p>Also define:</p>
<ul>
<li>FoundersRewardLastBlockHeight := max({ <em>height</em> ⦂ N | Halving(<em>height</em>) < 1 })</li>
</ul>
<p>Replace the first note in that section with:</p>
<ul>
<li>No Founders’ Reward is required to be paid for <em>height</em> > FoundersRewardLastBlockHeight (i.e. after the first halving), or for <em>height</em> = 0 (i.e. the genesis block).</li>
</ul>
<p>and in the second note, replace SlowStartShift + PreBlossomHalvingInterval - 1 with FoundersRewardLastBlockHeight.</p>
</section>
<section id="effect-on-difficulty-adjustment"><h3><span class="section-heading">Effect on difficulty adjustment</span><span class="section-anchor"> <a href="#effect-on-difficulty-adjustment"><img width="24" height="24" src="assets/images/section-anchor.png" alt=""></a></span></h3>
<p>The difficulty adjustment parameters PoWAveragingWindow and PoWMedianBlockSpan refer to numbers of blocks, but do <em>not</em> change at Blossom activation. This is because the amount of damping/averaging required is expected to be roughly the same, in terms of the number of blocks, after the change in block target spacing.</p>
<p>The change in the effective value of PoWTargetSpacing will cause the block spacing to adjust to the new target, at the normal rate for a difficulty adjustment. The results of simulations are consistent with this expected behaviour.</p>
<p>Note that the change in AveragingWindowTimespan(height) takes effect immediately when calculating the target difficulty starting from the block at the Blossom activation height, even though the difficulty of the preceding PoWAveragingWindow blocks will have been adjusted using the pre-Blossom target spacing. Therefore it is likely that the difficulty adjustment for the first few blocks after activation will be limited by PoWMaxAdjustDown. This is not anticipated to cause any problem.</p>
<section id="minimum-difficulty-blocks-on-the-test-network"><h4><span class="section-heading">Minimum difficulty blocks on the test network</span><span class="section-anchor"> <a href="#minimum-difficulty-blocks-on-the-test-network"><img width="24" height="24" src="assets/images/section-anchor.png" alt=""></a></span></h4>
<p>On the test network from block height 299188 onward, the difficulty adjustment algorithm allows minimum-difficulty blocks, as described in <a id="id11" class="footnote_reference" href="#zip-0205">5</a>, when the block time exceeds a given threshold. This specification changes this threshold to be proportional to the block target spacing.</p>
<p>That is, if the block time of a block at height <em>height</em> ≥ 299188 is at least 6 · PoWTargetSpacing(<em>height</em>) seconds after that of the preceding block, then the block is a minimum-difficulty block, and its target threshold is set to the value of PoWLimit for testnet (see <a id="id12" class="footnote_reference" href="#latest-protocol">1</a> section 5.3).</p>
<p>As before, the <code>nBits</code> field of a minimum-difficulty block is still computed according to the original difficulty adjustment algorithm, and only this field is used for the purpose of computing the MeanTarget values from which subsequent difficulty changes are calculated.</p>
</section>
</section>
<section id="non-consensus-node-behaviour"><h3><span class="section-heading">Non-consensus node behaviour</span><span class="section-anchor"> <a href="#non-consensus-node-behaviour"><img width="24" height="24" src="assets/images/section-anchor.png" alt=""></a></span></h3>
<section id="end-of-service-halt"><h4><span class="section-heading">End-of-Service halt</span><span class="section-anchor"> <a href="#end-of-service-halt"><img width="24" height="24" src="assets/images/section-anchor.png" alt=""></a></span></h4>
<p><cite>zcashd</cite> implements an "End-of-Service halt" behaviour that halts the node at a block height that corresponds approximately to a given time after release. This interval SHOULD be adjusted in releases where the End-of-Service halt time will follow Blossom activation.</p>
</section>
<section id="default-expiry-delta"><h4><span class="section-heading">Default expiry delta</span><span class="section-anchor"> <a href="#default-expiry-delta"><img width="24" height="24" src="assets/images/section-anchor.png" alt=""></a></span></h4>
<p>When not overridden by the <cite>-txexpirydelta</cite> option, <cite>zcashd</cite> RPC calls that create transactions use a default value for the number of blocks after which a transaction will expire. The default in recent versions of <cite>zcashd</cite> is 20 blocks, which at the pre-Blossom block target spacing corresponds to roughly 50 minutes.</p>
<p>This default SHOULD change to BlossomPoWTargetSpacingRatio · 20 blocks after Blossom activation, to maintain the approximate expiry time of 50 minutes.</p>
<p>If the <cite>-txexpirydelta</cite> option is set, then the set value SHOULD be used both before and after Blossom activation.</p>
</section>
<section id="sprout-to-sapling-migration"><h4><span class="section-heading">Sprout to Sapling migration</span><span class="section-anchor"> <a href="#sprout-to-sapling-migration"><img width="24" height="24" src="assets/images/section-anchor.png" alt=""></a></span></h4>
<p>ZIP 308 <a id="id13" class="footnote_reference" href="#zip-0308">7</a> defines a procedure for migrating funds from Sprout to Sapling z-addresses. In that procedure, migration transactions are sent every 500 blocks, which corresponded to roughly 20.83 hours before Blossom.</p>
<p>The 500-block constant has not been changed. Therefore, migration transactions are now sent roughly every 10.42 hours after Blossom activation. This has been noted in the ZIP, and a table showing the expected time to complete migration has been updated accordingly.</p>
</section>
<section id="fingerprinting-mitigation"><h4><span class="section-heading">Fingerprinting mitigation</span><span class="section-anchor"> <a href="#fingerprinting-mitigation"><img width="24" height="24" src="assets/images/section-anchor.png" alt=""></a></span></h4>
<p>A "fingerprinting attack" is a network analysis technique in which nodes are identified across network sessions, for example using information about which blocks they request or send.</p>
<p><code>zcashd</code> inherits from Bitcoin Core the following behaviour, described in a comment in <code>main.cpp</code>, intended as a fingerprinting mitigation:</p>
<pre>// To prevent fingerprinting attacks, only send blocks outside of the active
// chain if they are valid, and no more than a month older (both in time, and in
// best equivalent proof of work) than the best header chain we know about.</pre>
<p>We make no assertion about the significance of fingerprinting for Zcash, and (despite the word "prevent" in the above comment) no claim about the effectiveness of this mitigation.</p>
<p>In any case, to estimate the "best equivalent proof of work" of a given block chain (measured in units of time), we take the total work of the chain as defined in <a id="id14" class="footnote_reference" href="#latest-protocol">1</a> section 7.6.5, divide by the work of the block at the active tip, and multiply by the target block spacing of that block.</p>
<p>It is not a requirement of the Zcash protocol that this fingerprinting mitigation is used; however, if it is used, then it SHOULD use the target block spacing at the same block height that is used for the current work estimate.</p>
</section>
<section id="monitoring-for-quicker-or-slower-than-expected-blocks"><h4><span class="section-heading">Monitoring for quicker- or slower-than-expected blocks</span><span class="section-anchor"> <a href="#monitoring-for-quicker-or-slower-than-expected-blocks"><img width="24" height="24" src="assets/images/section-anchor.png" alt=""></a></span></h4>
<p><cite>zcashd</cite> previously did this monitoring every 150 seconds; it is now done every 60 seconds.</p>
</section>
<section id="block-timeout"><h4><span class="section-heading">Block timeout</span><span class="section-anchor"> <a href="#block-timeout"><img width="24" height="24" src="assets/images/section-anchor.png" alt=""></a></span></h4>
<p>The timeout for a requested block is calculated as the target block time, multiplied by 2 + (the number of queued validated headers)/2.</p>
</section>
<section id="latency-optimization-when-requesting-blocks"><h4><span class="section-heading">Latency optimization when requesting blocks</span><span class="section-anchor"> <a href="#latency-optimization-when-requesting-blocks"><img width="24" height="24" src="assets/images/section-anchor.png" alt=""></a></span></h4>
<p>When <code>zcashd</code> sees an announced block that chains from headers that it does not already have, it will first ask for the headers, and then the block itself. A latency optimization is performed only if the chain is "nearly synced":</p>
<pre>// First request the headers preceding the announced block. In the normal fully-synced
// case where a new block is announced that succeeds the current tip (no reorganization),
// there are no such headers.
// Secondly, and only when we are close to being synced, we request the announced block directly,
// to avoid an extra round-trip. Note that we must *first* ask for the headers, so by the
// time the block arrives, the header chain leading up to it is already validated. Not
// doing this will result in the received block being rejected as an orphan in case it is
// not a direct successor.</pre>
<p>The heuristic for "nearly synced" is that the timestamp of the block at the active tip is no more than 20 block times before the current "adjusted time". In <code>zcashd</code> this calculation uses the block target spacing as of the best known header. Around Blossom activation when the block target spacing changes, this could cause the heuristic to be based on the pre-Blossom block target spacing until the node has synced headers past the activation block, but this is not anticipated to cause any problem.</p>
</section>
<section id="response-to-getblocks-message-when-pruning"><h4><span class="section-heading">Response to getblocks message when pruning</span><span class="section-anchor"> <a href="#response-to-getblocks-message-when-pruning"><img width="24" height="24" src="assets/images/section-anchor.png" alt=""></a></span></h4>
<p>If pruning is enabled, when <code>zcashd</code> responds to an "getblocks" peer-to-peer message, it will only include blocks that it has on disk, and is likely to still have on disk an hour after responding to the message:</p>
<pre>// If pruning, don't inv blocks unless we have on disk and are likely to still have
// for some reasonable time window (1 hour) that block relay might require.</pre>
<p>For each block, when estimating whether it will still be on disk after an hour, we take MIN_BLOCKS_TO_KEEP = 288 blocks, minus approximately the number of blocks expected in one hour at the target block spacing as of that block. Around Blossom activation, this might underestimate the number of blocks in the next hour, but given the value of MIN_BLOCKS_TO_KEEP, this is not anticipated to cause any problem.</p>
</section>
<section id="estimation-of-fully-synced-chain-height"><h4><span class="section-heading">Estimation of fully synced chain height</span><span class="section-anchor"> <a href="#estimation-of-fully-synced-chain-height"><img width="24" height="24" src="assets/images/section-anchor.png" alt=""></a></span></h4>
<p><code>zcashd</code> uses the <code>EstimateNetHeight</code> function to estimate the approximate height of the fully synced chain, so that the progress of block download can be displayed to the node operator. This function has been rewritten, simplified, and changed to take account of cases where the time period that needs to be estimated crosses Blossom activation.</p>
</section>
<section id="other-block-related-constants"><h4><span class="section-heading">Other block-related constants</span><span class="section-anchor"> <a href="#other-block-related-constants"><img width="24" height="24" src="assets/images/section-anchor.png" alt=""></a></span></h4>
<p>The following constants, measured in number of blocks, were reviewed and a decision was made not to change them:</p>
<pre>/** The number of blocks within expiry height when a tx is considered to be expiring soon */
TX_EXPIRING_SOON_THRESHOLD = 3
/** Maximum reorg length we will accept before we shut down and alert the user. */
MAX_REORG_LENGTH = COINBASE_MATURITY - 1;
static const int COINBASE_MATURITY = 100;
/** Number of blocks that can be requested at any given time from a single peer. */
static const int MAX_BLOCKS_IN_TRANSIT_PER_PEER = 16;
static const unsigned int BLOCK_DOWNLOAD_WINDOW = 1024;
/** Block files containing a block-height within MIN_BLOCKS_TO_KEEP of chainActive.Tip() will not be pruned. */
static const unsigned int MIN_BLOCKS_TO_KEEP = 288;
/**
* The period before a network upgrade activates, where connections to upgrading peers are preferred (in blocks).
* This was three days for upgrades up to and including Blossom, and is 1.5 days from Heartwood onward.
*/
static const int NETWORK_UPGRADE_PEER_PREFERENCE_BLOCK_PERIOD = 1728;</pre>
</section>
</section>
</section>
<section id="deployment"><h2><span class="section-heading">Deployment</span><span class="section-anchor"> <a href="#deployment"><img width="24" height="24" src="assets/images/section-anchor.png" alt=""></a></span></h2>
<p>This proposal will be deployed with the Blossom network upgrade. <a id="id15" class="footnote_reference" href="#zip-0206">6</a></p>
</section>
<section id="backward-compatibility"><h2><span class="section-heading">Backward compatibility</span><span class="section-anchor"> <a href="#backward-compatibility"><img width="24" height="24" src="assets/images/section-anchor.png" alt=""></a></span></h2>
<p>This proposal intentionally creates what is known as a "bilateral consensus rule change". Use of this mechanism requires that all network participants upgrade their software to a compatible version within the upgrade window. Older software will treat post-upgrade blocks as invalid, and will follow any pre-upgrade consensus branch that persists.</p>
</section>
<section id="reference-implementation"><h2><span class="section-heading">Reference Implementation</span><span class="section-anchor"> <a href="#reference-implementation"><img width="24" height="24" src="assets/images/section-anchor.png" alt=""></a></span></h2>
<p><a href="https://github.com/zcash/zcash/pull/4025">https://github.com/zcash/zcash/pull/4025</a></p>
</section>
<section id="references"><h2><span class="section-heading">References</span><span class="section-anchor"> <a href="#references"><img width="24" height="24" src="assets/images/section-anchor.png" alt=""></a></span></h2>
<table id="latest-protocol" class="footnote">
<tbody>
<tr>
<th>1</th>
<td><a href="protocol/blossom.pdf">Zcash Protocol Specification, Version 2019.0.1 or later [Overwinter+Sapling+Blossom]</a></td>
</tr>
</tbody>
</table>
<table id="preblossom-protocol" class="footnote">
<tbody>
<tr>
<th>2</th>
<td><a href="https://github.com/zcash/zips/blob/9515d73aac0aea3494f77bcd634e1e4fbd744b97/protocol/protocol.pdf">Zcash Protocol Specification, Version 2018.0-beta-37 (exactly) [Overwinter+Sapling]</a></td>
</tr>
</tbody>
</table>
<table id="rfc2119" class="footnote">
<tbody>
<tr>
<th>3</th>
<td><a href="https://www.rfc-editor.org/rfc/rfc2119.html">Key words for use in RFCs to Indicate Requirement Levels</a></td>
</tr>
</tbody>
</table>
<table id="zip-0200" class="footnote">
<tbody>
<tr>
<th>4</th>
<td><a href="zip-0200">ZIP 200: Network Upgrade Mechanism</a></td>
</tr>
</tbody>
</table>
<table id="zip-0205" class="footnote">
<tbody>
<tr>
<th>5</th>
<td><a href="zip-0205">ZIP 205: Deployment of the Sapling Network Upgrade</a></td>
</tr>
</tbody>
</table>
<table id="zip-0206" class="footnote">
<tbody>
<tr>
<th>6</th>
<td><a href="zip-0206">ZIP 206: Deployment of the Blossom Network Upgrade</a></td>
</tr>
</tbody>
</table>
<table id="zip-0308" class="footnote">
<tbody>
<tr>
<th>7</th>
<td><a href="zip-0308">ZIP 308: Sprout to Sapling Migration</a></td>
</tr>
</tbody>
</table>
<table id="slowfastblocks" class="footnote">
<tbody>
<tr>
<th>8</th>
<td><a href="https://blog.ethereum.org/2015/09/14/on-slow-and-fast-block-times/">On Slow and Fast Block Times</a></td>
</tr>
</tbody>
</table>
</section>
</section>
</body>
</html>