<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <author>
    <name>Kevin Pan</name>
    <email>bit.kevin@gmail.com</email>
  </author>
  <generator uri="https://hexo.io/">Hexo</generator>
  <icon>https://www.gravatar.com/avatar/6cb7e9fa31fde563067700e5be533b44</icon>
  <id>https://kevinpan.page/</id>
  <link href="https://kevinpan.page/" rel="alternate"/>
  <link href="https://kevinpan.page/atom.xml" rel="self"/>
  <rights>All rights reserved 2026, Kevin Pan</rights>
  <subtitle>kevinpan.page</subtitle>
  <title>Kevin Pan's Page</title>
  <updated>2019-03-13T20:44:23.000Z</updated>
  <entry>
    <author>
      <name>Kevin Pan</name>
      <email>bit.kevin@gmail.com</email>
    </author>
    <content>
      <![CDATA[<p>关于签名算法的一些基本变量定义：</p><ul><li>G为椭圆曲线</li><li>随机值k，R&#x3D;kG</li><li>m，待签名消息哈希值</li><li>私钥x，公钥P&#x3D;xG</li><li>H()，哈希函数</li><li>r &#x3D; R.x，表示r为R的x坐标值</li></ul><h2 id="Schnorr"><a href="#Schnorr" class="headerlink" title="Schnorr"></a>Schnorr</h2><ul><li>签名：(R, s)</li><li>生成签名：<code>s = k + H(R||P||m)*x</code></li><li>验证签名：<code>sG = R + H(R||P||m)P</code></li></ul><h2 id="ECDSA"><a href="#ECDSA" class="headerlink" title="ECDSA"></a>ECDSA</h2><ul><li>签名：(r, s)</li><li>生成签名：<code> s = m/k + r/k*x</code></li><li>验证签名：<code>sR = mG + rP</code><ul><li>先求得R值：<code>R = m/s*G + r/s*P</code></li><li>再求得r值：<code>r = R.x</code> ，即R的x坐标值，验证r是否一致</li></ul></li></ul><h2 id="随机值k选取的重要性"><a href="#随机值k选取的重要性" class="headerlink" title="随机值k选取的重要性"></a>随机值k选取的重要性</h2><p>Schnorr与ECDSA均对随机值强依赖，若任意两次签名之间采用了相同的随机值k，则暴露出私钥。下面我们演算在随机值相同情况下，各个算法暴露私钥的过程。</p><h3 id="Schnorr暴露私钥过程"><a href="#Schnorr暴露私钥过程" class="headerlink" title="Schnorr暴露私钥过程"></a>Schnorr暴露私钥过程</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">两次签名，随机值k相同，同一个把公钥P，两次签名消息分别为：m1, m2。</span><br><span class="line">令 e1 = H(R||P||m1), e2 = H(R||P||m2)</span><br><span class="line"></span><br><span class="line">s1 = k + H(R||P||m1)*x = k + e1*x</span><br><span class="line">s2 = k + H(R||P||m2)*x = k + e2*x</span><br><span class="line"></span><br><span class="line">有两个签名：(R, s1), (R, s2)</span><br><span class="line"></span><br><span class="line">s1 - s2 = k + e1*x - (k + e2*x)</span><br><span class="line">        = (e1 - e2)*x</span><br><span class="line"></span><br><span class="line">那么私钥就可以得出：</span><br><span class="line">x = (s1 - s2) / (e1 - e2)</span><br></pre></td></tr></table></figure><h3 id="ECDSA暴露私钥过程"><a href="#ECDSA暴露私钥过程" class="headerlink" title="ECDSA暴露私钥过程"></a>ECDSA暴露私钥过程</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">两次签名，随机值k相同，同一个把公钥P，两次签名消息分别为：m1, m2。</span><br><span class="line"></span><br><span class="line">s1 = m1/k + r/k*x</span><br><span class="line">s2 = m2/k + r/k*x</span><br><span class="line"></span><br><span class="line">有两个签名：(r, s1), (r, s2)</span><br><span class="line"></span><br><span class="line">第一步，将上述两个等式相减，先暴露出该随机值k，有：</span><br><span class="line">k = (s1 - s2) / (m1 - m2)</span><br><span class="line"></span><br><span class="line">根据k值，计算出其对应的r值：</span><br><span class="line">r = R.x = (kG).x</span><br><span class="line"></span><br><span class="line">第二步，反推出私钥：</span><br><span class="line">x = (s1 - m1/k)*k/r 或</span><br><span class="line">x = (s2 - m2/k)*k/r</span><br></pre></td></tr></table></figure><p>根据上述过程，得出结论：相同私钥对任意数据签名，若任意两次签名采用相同的随机值，则立即暴露出私钥。</p><p>对于软硬件钱包来说，其随机数发生器将是非常重要的基石，绝不能出故障。</p><h2 id="伪签名问题"><a href="#伪签名问题" class="headerlink" title="伪签名问题"></a>伪签名问题</h2><h3 id="ECDSA伪签名"><a href="#ECDSA伪签名" class="headerlink" title="ECDSA伪签名"></a>ECDSA伪签名</h3><p>之所以说是伪签名，因为是可以验证通过的签名，并不是“无效签名”。CSW曾经多次使用此伎俩，制造中本聪公钥对应的伪签名，甚至骗过了一些不太清楚签名机制的人。</p><p>生成ECDSA的伪签名的过程：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">ECDSA验证签名的等式为：</span><br><span class="line">R = m/s*G + r/s*P</span><br><span class="line"></span><br><span class="line">令 u=m/s, v=r/s</span><br><span class="line">即 R = uG + vP</span><br><span class="line"></span><br><span class="line">随机生成u、v的值，则有：</span><br><span class="line">u&#x27;G + v&#x27;P = R&#x27;</span><br><span class="line"></span><br><span class="line">得到 r&#x27; = R&#x27;.x，// 求X坐标</span><br><span class="line">得到 s&#x27; = r&#x27;/v</span><br><span class="line">得到 m&#x27; = u*s&#x27;</span><br><span class="line"></span><br><span class="line">那么对于消息m&#x27;，其签名为：(r&#x27;, s&#x27;)。此签名是可以验证通过的，因为验证等式是平衡的。</span><br></pre></td></tr></table></figure><p>防范ECDSA伪签名非常简单：不可由签名方提供消息m值，必须由验证方提供。因为在伪签名里，m值是随机生成的，一旦m值由其他人提供，则签名方是无法构造出平衡等式的。或者说伪签名里签名方是提供不了m值的消息原文的。</p><p>也就是说，由你写一句话下来，交给CSW去签名，他就无法提供出中本聪公钥下能够验证的签名了。</p><h3 id="Schnorr是伪签名免疫的"><a href="#Schnorr是伪签名免疫的" class="headerlink" title="Schnorr是伪签名免疫的"></a>Schnorr是伪签名免疫的</h3><p>Schnorr目前是无法构造出伪签名的，因为无法构造出平衡的验证等式。在Schnorr的验证等式中<code>sG = R + H(R||P||m)P</code>：</p><ul><li>若尝试随机等式左侧值（即随机s值），则无法找到合适的R’和m’，因为R值在哈希函数里使用了</li><li>若尝试随机等式右侧值（随机生成R和m值），<code>sG</code>是椭圆曲线乘法，曲线除法不可逆的情况下是无法找到对应的s’值</li></ul><p>因此Schnorr签名算法是无法构造出能验证通过的伪签名的。</p><hr><p>参考：</p><ul><li><a href="https://github.com/sipa/bips/blob/bip-schnorr/bip-schnorr.mediawiki">https://github.com/sipa/bips/blob/bip-schnorr/bip-schnorr.mediawiki</a></li><li>How Perfect Offline Wallets Can Still Leak Bitcoin Private Keys<ul><li><a href="https://bitcointalk.org/index.php?topic=883793.0">https://bitcointalk.org/index.php?topic=883793.0</a><ul><li><a href="https://bitcointalk.org/index.php?topic=285142.msg3077694#msg3077694">https://bitcointalk.org/index.php?topic=285142.msg3077694#msg3077694</a></li><li><a href="https://www.mail-archive.com/bitcoin-development@lists.sourceforge.net/msg02721.html">https://www.mail-archive.com/bitcoin-development@lists.sourceforge.net/msg02721.html</a></li></ul></li></ul></li></ul>]]>
    </content>
    <id>https://kevinpan.page/2019/03/13/important-random-k-and-fake-signatures/</id>
    <link href="https://kevinpan.page/2019/03/13/important-random-k-and-fake-signatures/"/>
    <published>2019-03-13T20:44:23.000Z</published>
    <summary>
      <![CDATA[<p>关于签名算法的一些基本变量定义：</p>
<ul>
<li>G为椭圆曲线</li>
<li>随机值k，R&#x3D;kG</li>
<li>m，待签名消息哈希值</li>
<li>私钥x，公钥P&#x3D;xG</li>
<li>H()，哈希函数</li>
<li>r &#]]>
    </summary>
    <title>随机数对签名的重要性与伪签名的构造</title>
    <updated>2019-03-13T20:44:23.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Kevin Pan</name>
      <email>bit.kevin@gmail.com</email>
    </author>
    <content>
      <![CDATA[<p>Taproot的概念由Gregory Maxwell在2018年01月提出，<a href="https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2018-January/015614.html">Taproot: Privacy preserving switchable scripting</a>，这个概念主要是他研究一番MAST后，发现可以与<a href="https://kevinpan.page/2019/02/28/schnorr-sigature/">Schnorr签名</a>一起构成一个特别简洁的输出模式。</p><h2 id="定义"><a href="#定义" class="headerlink" title="定义"></a>定义</h2><p>Taproot定义了一个输出，两个执行路径的实现方式。</p><ul><li>N个参与方，构成组公钥C：<code>C = A + B + … + N</code></li><li>N + 1个参与方，构成输出组公钥P：<code>P = C + H(C||S)G</code><ul><li>多出的一个参与方是：<code>H(C||S)</code>，组公钥C和脚本S的组合哈希。</li></ul></li><li>交易输出中填入组公钥P，格式类似Segwit：<code>[ver] [P]</code><ul><li>ver&#x3D;0，segwit已经使用了。Taproot可用ver&#x3D;1，软分叉来实现。</li></ul></li><li>花费路径有两个，二选一：<ol><li>签名模式：N+1参与方全部签名<ul><li>可以聚合出组公钥P的签名。验证签名后即可花费，无需暴露脚本</li></ul></li><li>脚本模式：N个参与方里，任意一个或以上拒绝签名。则走脚本模式：<ul><li>提供：组公钥C、脚本S、以及脚本S相应的数据，即可以花费</li></ul></li></ol></li></ul><h2 id="示例说明"><a href="#示例说明" class="headerlink" title="示例说明"></a>示例说明</h2><p>为了搞清楚运作过程，我们举例说明一下。</p><ul><li><p>两个参与方，分别持有公钥A、B。那么其组公钥（聚合公钥）就是：<code>C = A + B</code>。</p></li><li><p>定义脚本S：<code>&lt;timeout&gt; OP_CSV OP_DROP B OP_CHECKSIGVERIFY</code>，这个脚本的含义是“N天过后，B一个人签名就可以花掉这个输出”。</p></li></ul><p>那么把上面几个东西拧一起，定义一个新的公钥：<code>P = C + H(C||S)G</code>。</p><p>放到输出里，<code>[version] [P]</code>:</p><ul><li>version，版本号，用于表示taproot的版本</li><li>P：公钥</li></ul><p>令：<code>d = H(C||S)</code>，则有<code>H(C||S)G = dG = D</code> 。那么：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">P = C + H(C||S)G</span><br><span class="line">  = C + D</span><br><span class="line">  = A + B + D</span><br><span class="line">  = aG + bG + dG</span><br></pre></td></tr></table></figure><p>本例其实是三个私钥在参与，只是第三个私钥d是双方共同持有的，外界仅知道组公钥P。</p><p>Taproot的签名算法采用Schnorr Signature，然后定义两个花掉此输出的方式。</p><h3 id="方式一、签名模式花费"><a href="#方式一、签名模式花费" class="headerlink" title="方式一、签名模式花费"></a>方式一、签名模式花费</h3><p>合作模式利用了Schnorr签名的线性特性，若<code>P = A + B + D</code>，则公钥A、B、D的单独签名叠加起来就是公钥P的签名。</p><p>A、B都签名（D必然签名，因为私钥d是双方共享的，任何一方都可完成D签名），那么把A、B、D三个签名加起来就是P的签名。</p><p>双方合作的话，外界看上去就是一个正常的P的签名一样，这个交易也与普通的转账完全一样。其脚本信息则完全隐蔽掉了。</p><p>当然，参与方不限定两个，可以是N个。</p><h3 id="方式二、脚本模式花费"><a href="#方式二、脚本模式花费" class="headerlink" title="方式二、脚本模式花费"></a>方式二、脚本模式花费</h3><p>若A或者B任何一方拒绝签名，则无法产生（叠加出）公钥P的签名（此时仅有A+D的签名，或者B+D的签名），也就无法花费此输出。</p><p>假设A拒绝提供签名，那么B就无法合成出P的签名，但B可选择执行脚本进行花费。B需要提供的有：</p><ul><li>公钥C</li><li>脚本S</li><li>符合脚本S的执行数据DATA（本例是B的签名）</li></ul><p>其他节点收到这样一笔交易后，验证过程：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">1. 计算出私钥d: d = H(C||S)</span><br><span class="line"></span><br><span class="line">2. 计算出d的公钥: D = dG</span><br><span class="line"></span><br><span class="line">3. 验证公钥: P == C + D</span><br><span class="line"></span><br><span class="line">4. 构建出完整脚本：DATA || S</span><br><span class="line"></span><br><span class="line">5. 执行脚本，并判断结果是否为True</span><br><span class="line"></span><br><span class="line">// 上述过程都顺利通过的，则可以花费。</span><br></pre></td></tr></table></figure><p>通常，脚本通常都是带延迟条件的，具备一定惩罚性质，例如N天后可以花费，或者多少高度后才可以进块等。当然，也可以不设置延迟条件，脚本内容是没有任何限定的。</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>Taproot只有在非合作时下才会暴露并执行脚本，签名模式下看上去就是一个再普通不过的公钥签名交易而已。</p><ul><li>在Taproot里，是N方参与，N通常&gt;&#x3D;2。（N&#x3D;1技术上可行，但没有太大意义）<ul><li>N方参与，则必须有N方签名，签名数量小于N则无法验证通过。缺任意签名则合作模式失败。</li></ul></li><li>两个分支其实已经可以覆盖很多场景，甚至大部分场景。</li><li>一般来说，N方大概率都是合作的。</li></ul><p>Taproot一个典型应用场景就是闪电网络的交易。当然，Taproot是灵活的，更多应用场景等待挖掘。</p><hr><p>参考：</p><ul><li>[bitcoin-dev] Taproot: Privacy preserving switchable scripting<ul><li><a href="https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2018-January/015614.html">https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2018-January/015614.html</a></li></ul></li></ul>]]>
    </content>
    <id>https://kevinpan.page/2019/03/01/Taproot/</id>
    <link href="https://kevinpan.page/2019/03/01/Taproot/"/>
    <published>2019-03-01T17:57:05.000Z</published>
    <summary>
      <![CDATA[<p>Taproot的概念由Gregory Maxwell在2018年01月提出，<a href="https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2018-January/015614.html">Taproot:]]>
    </summary>
    <title>Taproot简介</title>
    <updated>2019-03-01T17:57:05.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Kevin Pan</name>
      <email>bit.kevin@gmail.com</email>
    </author>
    <content>
      <![CDATA[<p>Schnorr签名算法是由德国数学家、密码学家Claus Schnorr提出。并于1990年申请了专利，<a href="https://www.google.com/patents/US4995082">U.S. Patent 4,995,082</a>，该专利与2008年2月失效。目前该算法可以自由使用。</p><p>Schnorr签名算法几乎在各个层面均优于比特币现有的签名算法ECDSA：性能，安全，体积，扩展性等方面。</p><p>Schnorr Sig可以与ECDSA使用同一个椭圆曲线：<a href="http://www.secg.org/sec2-v2.pdf">secp256k1 curve</a>，升级起来的改动非常小。</p><h2 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h2><p>我们定义几个变量：</p><ul><li>G：椭圆曲线。</li><li>m：待签名的数据，通常是一个32字节的哈希值。</li><li>x：私钥。<code>P = xG</code>，P为x对应的公钥。</li><li>H()：哈希函数。<ul><li>示例：写法<code>H(m || R || P)</code>可理解为：将m, R, P三个字段拼接在一起然后再做哈希运算。</li></ul></li></ul><h3 id="生成签名"><a href="#生成签名" class="headerlink" title="生成签名"></a>生成签名</h3><p>签名者已知的是：G-椭圆曲线, H()-哈希函数，m-待签名消息, x-私钥。</p><ol><li>选择一个随机数<code>k</code>, 令 <code>R = kG</code></li><li>令 <code>s = k + H(m || R || P)*x</code></li></ol><p>那么，公钥P对消息m的签名就是：<code>(R, s)</code>，这一对值即为Schnorr签名。</p><h3 id="验证签名"><a href="#验证签名" class="headerlink" title="验证签名"></a>验证签名</h3><p>验证者已知的是：G-椭圆曲线, H()-哈希函数，m-待签名消息, P-公钥，(R, s)-Schnorr签名。验证如下等式：</p><p><code>sG = R + H(m || R || P)P</code></p><p>若等式成立，则可证明签名合法。</p><p>我们推演一下，此过程包含了一个极其重要的理论：椭圆曲线无法进行除法运算。</p><ol><li>s值的定义：<code>s = k + H(m || R || P)*x</code>，等式两边都乘以椭圆曲线G，则有：</li><li><code>sG = kG + H(m || R || P)*x*G</code>，又因<code>R = kG, P = xG</code>，则有：</li><li><code>sG = R + H(m || R || P)P</code>，椭圆曲线无法进行除法运算，所以第3步的等式，无法向前反推出第1步，就不会暴露k值以及x私钥。同时，也完成了等式验证。</li></ol><h3 id="组签-Group-Signature"><a href="#组签-Group-Signature" class="headerlink" title="组签, Group Signature"></a>组签, Group Signature</h3><p>一组公钥，N把，签名后得到N个签名。这个N个签名是可以相加的，最终得到一个签名。这个签名的验证通过，则代表N把公钥的签名全部验证通过。</p><p>有：</p><ul><li>椭圆曲线：G</li><li>待签名的数据：m</li><li>哈希函数：H()</li><li>私钥：x1，x2，公钥：P1&#x3D;x1*G, P2&#x3D;x2*G</li><li>随机数：k1, k2，并有 R1&#x3D;k1*G, R2&#x3D;k2*G</li><li>组公钥：P &#x3D; P1 + P2</li></ul><p>则有：</p><ul><li>私钥x1和x2的签名为：(R1, s1), (R2, s2)。</li><li>两个签名相加得到组签名：(R, s)。其中：<code>R = R1 + R2, s = s1 + s2</code>。</li></ul><p>推演过程：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">1. 令 R = R1 + R2, s = s1 + s2</span><br><span class="line"></span><br><span class="line">2. 已知：s1 = k1 + H(m || R || P)*x1，s2 = k2 + H(m || R || P)*x2</span><br><span class="line"></span><br><span class="line">3. s = s1 + s2</span><br><span class="line">     = k1 + H(m || R || P)*x1 +</span><br><span class="line">       k2 + H(m || R || P)*x2</span><br><span class="line">     = (k1 + k2) + H(m || R || P)(x1 + x2)</span><br><span class="line"></span><br><span class="line">4. 两边同时乘以G，则有：</span><br><span class="line">    sG = (k1  + k2)G + H(m || R || P)(x1  + x2)G</span><br><span class="line">       = (k1G + k2G) + H(m || R || P)(x1G + x2G)</span><br><span class="line">       = (R1 + R2) + H(m || R || P)(P1 + P2)</span><br><span class="line">       = R + H(m || R || P)P</span><br><span class="line"></span><br><span class="line">5. 完成证明，并从两个合作方推演至N个合作方</span><br></pre></td></tr></table></figure><p>组公钥(Group Key)，是N把公钥进行相加后的值，又称聚合公钥(Aggregation Key)。需要指出的是，参与方需要先相互交换公钥和R值，然后再进行各自的签名。</p><h2 id="应用"><a href="#应用" class="headerlink" title="应用"></a>应用</h2><p>若使用在比特币上，相比ECDSA会有一些额外的显著优势：</p><ul><li>更安全。目前Schnorr签名有<a href="https://www.di.ens.fr/david.pointcheval/Documents/Papers/2000_joc.pdf">安全证明</a>，而ECDSA目前并没有类似的证明。</li><li>无延展性困扰。ECDSA签名是可延展性的，第三方无需知道私钥，可以直接修改既有签名，依然能够保持该签名对于此交易是有效的。比特币一直存在延展性攻击，直到SegWit激活后才修复，前提是使用segwit交易，而不是传统交易。<a href="https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki">BIP62</a> 和 <a href="https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki">BIP66</a> 对此有详细描述。</li><li>线性。Schnorr签名算法是线性的！这点非常牛逼，基于这点可衍生出许多应用。例如，N个公钥进行签名，采用ECDSA的话，则有N个签名，验证同样需要做N次。若使用Schnorr，由于线性特性，则可以进行签名叠加，仅保留最终的叠加签名。例如同一个交易无论输入数量多少，其均可叠加为一个签名，一次验证即可。以及GMaxwell提出的Taproot&#x2F;Grafroot也是基于其线性特性。</li></ul><h2 id="Q-A"><a href="#Q-A" class="headerlink" title="Q&amp;A"></a>Q&amp;A</h2><p>Q: Schnorr签名是否可以用在m of n多重签名上？<br>A: 当然可以。多重签名只是m of n的签名数量的模式。与签名算法无关。</p><p>Q: Schnorr的组签名特性是否可以做或模拟出m of n式的签名？<br>A: 无法做到。组内有N把公钥，则必须对应有N个签名，缺一不可。每个人在生成签名的时候，在哈希函数里都代入的都是组公钥P。</p><p>Q: 签名机制的安全性如何衡量？<br>A: 主要取决于两个：1. 签名算法本身 2. 椭圆曲线。目前，Schnorr与ECDSA都用的是曲线secp256k1，这个层面一样。至于签名算法本身安全性，Schnorr目前有安全证明，安全优于ECDSA。</p><hr><p>参考：</p><ul><li><p>Schnorr signature，<a href="https://en.wikipedia.org/wiki/Schnorr_signature">https://en.wikipedia.org/wiki/Schnorr_signature</a></p></li><li><p>BIP-Schnorr,Pieter Wuille，<a href="https://github.com/sipa/bips/blob/bip-schnorr/bip-schnorr.mediawiki">https://github.com/sipa/bips/blob/bip-schnorr/bip-schnorr.mediawiki</a></p></li><li><p>Simple Schnorr Multi-Signatures with Applications to Bitcoin，<a href="https://eprint.iacr.org/2018/068">https://eprint.iacr.org/2018/068</a></p></li></ul>]]>
    </content>
    <id>https://kevinpan.page/2019/02/28/schnorr-sigature/</id>
    <link href="https://kevinpan.page/2019/02/28/schnorr-sigature/"/>
    <published>2019-02-28T19:24:52.000Z</published>
    <summary>
      <![CDATA[<p>Schnorr签名算法是由德国数学家、密码学家Claus Schnorr提出。并于1990年申请了专利，<a href="https://www.google.com/patents/US4995082">U.S. Patent 4,995,082</a>，该专利与2008]]>
    </summary>
    <title>Schnorr签名介绍</title>
    <updated>2019-02-28T19:24:52.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Kevin Pan</name>
      <email>bit.kevin@gmail.com</email>
    </author>
    <content>
      <![CDATA[<h2 id="写在前面的话"><a href="#写在前面的话" class="headerlink" title="写在前面的话"></a>写在前面的话</h2><p>个人认为比特币脚本发展有三个阶段：</p><p>第一阶段，原始脚本阶段，仅有脚本。典型的有：P2PH, P2PKH。<code>Script = scriptSig + scriptPubKey</code>构成。所有数据都是显式的，有诸多限制的。脚本升级比较困难。</p><p>第二阶段，半自由阶段：脚本+脚本哈希。脚本形式有所突破，变得更加灵活。从<a href="https://kevinpan.page/2019/02/12/p2sh/">P2SH</a>开始，以及后面的<a href="https://kevinpan.page/2019/02/21/BIP141/">Segwit</a>，都是采用这种形式构成，特征是输出里仅存放执行脚本的哈希。这一阶段的脚本依然受到一些限制，大小和OP操作数等，但灵活性已经大大增强，可轻松软分叉升级。脚本执行过程也变为两步：1. 验证哈希 2. 执行子脚本。</p><ul><li>P2SH: 输出中存放的是<code>RedeemScript</code>脚本哈希</li><li>SegWit: 输出中存放的是<code>witness</code>脚本哈希</li></ul><p>第三阶段，自由阶段：脚本+脚本哈希+语法树。在第二阶段的基础上，发展为语法树。输出依然存储的是哈希，但进化为脚本语法树的根哈希值，即MAST结构。MAST突破了诸多限制，大小和OP限制等。灵活性，扩展性均极大增强，可衍生出丰富的应用场景。MAST目前依然是处于<code>提议</code>阶段，尚未实施。</p><h2 id="比特币的脚本输出"><a href="#比特币的脚本输出" class="headerlink" title="比特币的脚本输出"></a>比特币的脚本输出</h2><p>比特币通过定义一系列OP码，将输入、输出的脚本压栈运行，根据运行结果判定花费行为是否合法。通常花费条件全部记录在交易的输出中。这个模式有一些缺点：</p><ol><li>接受者很难指定条件</li></ol><ul><li>当前主要是匹配公钥或者其哈希，然后花费时校验其签名</li></ul><ol><li>大脚本占用更多的UTXO空间</li><li>发送者付出更高的成本：交易体积大，通常需要付出更高的手续费</li><li>为了防止潜在的DoS攻击，比特币对脚本设定了一些限制：脚本体积小于10KBytes，或小于201个OP码</li><li>未执行的部分脚本，其非共识层面的数据都是公开的，即占用区块空间又暴露隐私</li></ol><p>P2SH的提出，解决了上述1、2、3三个问题。P2SH分离出RedeemScript，将其20字节Hash值放入输出，花费时提供出RedeemScript并执行。但依然有520字节限制，并需要公开完整的RedeemScript。</p><p>在Segwit里，定义了一个新的脚本输出格式，P2WSH，pay to witness script hash。与P2SH非常类似，但hash是32字节的，script存放在witness字段中（而不是原来的scriptSig字段）。</p><h2 id="Merkelized-Abstract-Syntax-Tree"><a href="#Merkelized-Abstract-Syntax-Tree" class="headerlink" title="Merkelized Abstract Syntax Tree"></a>Merkelized Abstract Syntax Tree</h2><h3 id="抽象语法树，-Abstract-Syntax-Tree"><a href="#抽象语法树，-Abstract-Syntax-Tree" class="headerlink" title="抽象语法树， Abstract Syntax Tree"></a>抽象语法树， Abstract Syntax Tree</h3><p>抽象语法树，或语法树，计算里非常常见。通过树形来表达编程语言的一种结构。</p><p>例如这段的代码展开后的语法树：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">while b ≠ 0</span><br><span class="line">    if a &gt; b</span><br><span class="line">        a := a − b</span><br><span class="line">    else</span><br><span class="line">        b := b − a</span><br><span class="line">    return a</span><br></pre></td></tr></table></figure><p><img src="https://user-images.githubusercontent.com/514951/53312467-1a88f300-38f0-11e9-9403-18a6ab033000.png" alt="400px-abstract_syntax_tree_for_euclidean_algorithm svg"></p><p>MAST也是采用树状结构的一种语法树，但更加精简：去掉条件判断，每个独立脚本就是一个叶子节点，每一个叶子节点就是一个执行分支。</p><h3 id="MAST"><a href="#MAST" class="headerlink" title="MAST"></a>MAST</h3><p>MAST的作者是Johnson Lau，于2016年04月由BIP114提出，相关贡献者有：Russell O’Connor, Pieter Wuille和Peter Todd。</p><p>MAST采用Merkle树将各个分支编码进脚本(Script)。花费时，仅需要提供执行的分支脚本和相关层的hash进行验证，不执行的分支脚本无需公开。需要验证的哈希层数是O(log N)复杂度，假设可执行的脚本分支多达1024个，那么层数也仅有10层，依然可以保持很小的体积。因无需展示出所有的脚本，实际也突破了脚本大小的520字节硬限制。</p><p>值得注意的是：MAST是二叉树，但无需平衡构造，形状任意。</p><p>在BIP141（Segregated Witness）里，设定了交易输出witness program的格式：</p><p><code>[version][program]</code></p><p>这里我们设定MAST类型输出的version为1，program的大小为32字节，其中witness program即为<code>MAST Root</code>。</p><p>欲花费这种的输入，则输入witness为：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">Script_stack_1</span><br><span class="line">Script_stack_2</span><br><span class="line">.</span><br><span class="line">.</span><br><span class="line">Script_stack_X (X ≥ 0)</span><br><span class="line">Subscript_1</span><br><span class="line">Subscript_2</span><br><span class="line">.</span><br><span class="line">.</span><br><span class="line">Subscript_Y (1 ≤ Y ≤ 255)</span><br><span class="line">Position</span><br><span class="line">Path</span><br><span class="line">Metadata (Y|MAST Version)</span><br></pre></td></tr></table></figure><p>这些字段可以划分为三个部分：</p><ol><li>脚本栈元素，即入栈的操作码和数据，早于<code>MAST Script</code>入栈</li><li>sub_script，子脚本，<code>MAST Script</code>会拆分为N个子脚本，拆分规则非常的自由，当然也可以选择不拆分</li><li>三个描述字段：<code>Postion</code>, <code>Path</code>, <code>Metadata</code></li></ol><h3 id="MAST-witness中各字段含义"><a href="#MAST-witness中各字段含义" class="headerlink" title="MAST witness中各字段含义"></a>MAST witness中各字段含义</h3><p><code>Metadata</code>的长度是1~5字节：</p><ul><li>第一个字节表示<code>Subscript</code>的数量，范围是1~255</li><li>接下来的N个字节(0&lt;&#x3D;N&lt;&#x3D;4)表示<code>MAST version</code>版本号，若版本为零，则可省略</li></ul><p><code>Path</code>表示Merkle树路径，用于<code>Script Hash</code></p><ul><li><code>Path</code>大小必须是32的整数倍，但不超过1024字节</li><li>每个32字节，是各层与这个路径相关分支的哈希值，哈希函数是double-sha256</li></ul><p><code>Position</code>表示叶子的位置，左侧开始，从零开始计数，长度通常小于4字节。若树的深度是4，那么可选范围是[0, 2^n-1]即[0, 15]。</p><p><code>Script Hash</code>的定义：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Script Hash = H(Y|H(Subscript_1)|H(Subscript_2)|...|H(Subscript_Y))</span><br><span class="line">H() = SHA256(SHA256())</span><br></pre></td></tr></table></figure><ul><li><code>Y</code>是一个字节的数，表示脚本数量</li><li>后面紧接着是每个子脚本的哈希值</li><li>把N个<code>SubScript_n</code>连接起来就是完整的<code>MAST Script</code>，这里可以自由的对<code>MAST Script</code>进行拆分，增强隐私性</li></ul><p><code>Script Root</code>就是脚本树的树根哈希值。</p><p><code>MAST Root</code>是<code>H(MAST Version|Script Root)</code>，数据长度是固定的36字节：前4字节表示版本，后32字节是脚本树的根哈希值。执行脚本前，需要验证<code>MAST Root</code>值，不相等则执行失败。</p><p>若<code>MAST Root</code>哈希值验证相等，<code>MAST Version</code>大于零，则脚本无需执行便直接返回成功。<code>SigOpsCost</code>为零，这个设定可以用于未来的脚本升级。</p><h2 id="约定"><a href="#约定" class="headerlink" title="约定"></a>约定</h2><p>在BIP114中所表述的MAST，其实是限制版本。广义的MAST，可以自由选择脚本组合，构成更加复杂的情况。</p><p>我们先看看在“广义MAST”下的情况，假设一个花费条件可以设定为：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(A or B) and (C or D or E) and (F or G)</span><br></pre></td></tr></table></figure><p>A~G一共七个脚本，构成三个主要条件（本例都是与运算），每个条件由各子脚本再布尔运算后返回结果。七个可构成一个三层的Merkle树，2^2&#x3D;4 &lt; 7 &lt; 2^3&#x3D;8。</p><p>在BIP114里，则限定为仅可执行单一脚本分支，那么欲实现上面的花费条件，则需要展开每个可能性，一共<code>2*3*2=12</code>个分支，则需要构造一个四层树，2^3&#x3D;8 &lt; 12 &lt; 2^4&#x3D;16。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">A and C and F</span><br><span class="line">B and C and F</span><br><span class="line">A and D and F</span><br><span class="line">.</span><br><span class="line">.</span><br><span class="line">B and E and G</span><br></pre></td></tr></table></figure><p>也就是说，即使不允许脚本之间进行组合逻辑运算，通常也不过就是树多了一层深度而已。</p><p>实现广义MAST的一个方法是组合使用几个操作码：<code>OP_EVAL</code>, <code>OP_CAT</code>, <code>OP_HASH256</code>。不过，<code>OP_EVAL</code>的引入会带来诸多问题，例如可能陷入死循环、无法做静态程序分析。</p><p>当前设计方案的一些优点：</p><ul><li><code>SubScript</code>位于特定的堆栈位置，可以做静态程序分析，可以统计<code>SigOps</code>操作数，可以提前终止脚本如果遇到非法OP等</li><li>不同的参与方仅需公布脚本哈希，H(SubScript)，直到花费时暴露完整脚本，带来更好的隐私性</li><li>如果需要公开最终的脚本，可以组合进一个子脚本里SubScript，稍微节约一些<code>SigOps</code>操作数</li></ul><p>也有一些缺点：</p><ul><li>例如需要展开各种条件，以形成某个分支</li><li>创建和存储MAST可能花费一些时间和空间，但这个仅影响合约中的相关参与方，不影响其他比特币用户</li></ul><h3 id="MAST-Version"><a href="#MAST-Version" class="headerlink" title="MAST Version"></a>MAST Version</h3><p>当前的提案中允许用户自己制定版本，比起<code>scriptPubKey</code>，<code>scriptSig</code>会相对廉价一些。未定义的版本，则依然保持人人可花费的特性，可用于未来做软分叉等。新版本可放宽限制(例如放开MAST Script的10K字节限制)，添加或者重定义OP码，甚至在未来引入其他脚本语言和系统。</p><h2 id="一个示例"><a href="#一个示例" class="headerlink" title="一个示例"></a>一个示例</h2><p><img src="https://raw.githubusercontent.com/bitcoin/bips/master/bip-0114/mastexample.png" alt="Calculation of MAST Root"></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line">Subscript:</span><br><span class="line">    SA = OP_1 OP_EQUALVERIFY (0x5188)</span><br><span class="line">    SB = OP_2 OP_EQUALVERIFY (0x5288)</span><br><span class="line">    SC = OP_3 OP_EQUALVERIFY (0x5388)</span><br><span class="line">    SD = OP_4 OP_EQUALVERIFY (0x5488)</span><br><span class="line">    SE = OP_5 OP_EQUALVERIFY (0x5588)</span><br><span class="line">    SF = OP_6 OP_EQUALVERIFY (0x5688)</span><br><span class="line">    SG = OP_7 OP_EQUALVERIFY (0x5788)</span><br><span class="line">    SH = OP_8 OP_EQUALVERIFY (0x5888)</span><br><span class="line">    M  = OP_RETURN &quot;Hello&quot;   (0x6a0548656c6c6f)</span><br><span class="line"></span><br><span class="line">Hash:</span><br><span class="line">    //</span><br><span class="line">    // Script Hash</span><br><span class="line">    //</span><br><span class="line">    HA = H(0x01|H(SA))             = H(0x015acb54166e0db370cd1b05a29120373568dacea2abc3748459ec3da2106e4b4e) = 0xd385d7268ad7e1ec51660f833d54787d2d8d79b6b1809d9c1d06c9e71f7be204</span><br><span class="line">    HB = H(0x02|H(SB)|H(SC))       = 0x7cbfa08e44ea9f4f996873be95d9bffd97d4b91a5af32cc5f64efb8461727cdd</span><br><span class="line">    HF = H(0x03|H(SD)|H(SE)|H(SF)) = 0x4611414355945a7c2fcc62a53a0004821b87e68f93048ffba7a55a3cb1e9783b</span><br><span class="line">    HG = H(0x01|H(SG))             = 0xaa5fbdf58264650eadec33691ba1e7606d0a62f570eea348a465c55bc86ffc10</span><br><span class="line">    HC = H(0x01|H(M))              = 0x70426d480d5b28d93c5be54803681f99abf4e8df4eab4dc87aaa543f0d138159</span><br><span class="line">    HD = H(0x0x|H(SH))             = 0x8482f6c9c3fe90dd4d533b4efedb6a241b95ec9267d1bd5aaaee36d2ce2dd6da</span><br><span class="line">    </span><br><span class="line">    //</span><br><span class="line">    // Node Hash</span><br><span class="line">    //</span><br><span class="line">    HE = H(HA|HB) = 0x049b9f2f94f0a9bdea624e39cd7d6b27a365c6a0545bf0e9d88d86eff4894210</span><br><span class="line">    HH = H(HC|HD) = 0xc709fdc632f370f3367da45378d1cf430c5fda6805e731ad5761c213cf2d276e</span><br><span class="line">    HI = H(HE|HF) = 0xead5e1a1e7e41b77b794f091df9be3f0e9f41d47304eb43dece90688f69843b7</span><br><span class="line">    HJ = H(HG|HH) = 0xd00fc690c4700d0f983f9700740066531ea826b21a4cbc62f80317261723d477</span><br><span class="line"></span><br><span class="line">    //</span><br><span class="line">    // Root Hash</span><br><span class="line">    //</span><br><span class="line">    Script Root = H(HI|HJ) = 0x26d5235d20daf1440a15a248f5b5b4f201392128072c55afa64a26ccc6f56bd9</span><br><span class="line"></span><br><span class="line">    MAST Root = H(MAST Version|Script Root) </span><br><span class="line">              = H(0x0000000026d5235d20daf1440a15a248f5b5b4f201392128072c55afa64a26ccc6f56bd9)</span><br><span class="line">              = 0xb4b706e0c02eab9aba58419eb7ea2a286fb1c01d7406105fc12742bf8a3f97c9</span><br></pre></td></tr></table></figure><p>交易输出（即scriptPubKey）为：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">scriptPubKey:</span><br><span class="line">0x5120b4b706e0c02eab9aba58419eb7ea2a286fb1c01d7406105fc12742bf8a3f97c9</span><br><span class="line"></span><br><span class="line">// 0x51，即OP_1</span><br><span class="line">// 0x20=32，表示witness_program为32字节长度</span><br><span class="line">// 0xb4b706...3f97c9 即MAST Root Hash</span><br><span class="line">//</span><br><span class="line">// Decode:</span><br><span class="line">//   OP_1 0xb4b706e0c02eab9aba58419eb7ea2a286fb1c01d7406105fc12742bf8a3f97c9</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>Witness为：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">Script_stack_1: 0x06</span><br><span class="line">Script_stack_2: 0x05</span><br><span class="line">Script_stack_3: 0x04</span><br><span class="line">Subscript_1:    0x5488</span><br><span class="line">Subscript_2:    0x5588</span><br><span class="line">Subscript_3:    0x5688</span><br><span class="line">Position:       0x01 (HF is the second hash in its level)</span><br><span class="line">Path (HE|HJ):   0x049b9f2f94f0a9bdea624e39cd7d6b27a365c6a0545bf0e9d88d86eff4894210d00fc690c4700d0f983f9700740066531ea826b21a4cbc62f80317261723d477</span><br><span class="line">Metadata:       0x03 (3 Subscript, version is 0 so ignore it)</span><br></pre></td></tr></table></figure><h3 id="非平衡式构造"><a href="#非平衡式构造" class="headerlink" title="非平衡式构造"></a>非平衡式构造</h3><p>在构造脚本树时，可以把执行概率大的脚本放在接近树根的层，执行概率低的分支脚本放到远离树根的层。这种有意的不平衡构造方式，可以有效减少花费时witness的体积。</p><h3 id="多重签名和过期"><a href="#多重签名和过期" class="headerlink" title="多重签名和过期"></a>多重签名和过期</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">IF</span><br><span class="line">    2 &lt;Alice&#x27;s pubkey&gt; &lt;Bob&#x27;s pubkey&gt; &lt;Escrow&#x27;s pubkey&gt; 3 CHECKMULTISIG</span><br><span class="line">ELSE</span><br><span class="line">    &quot;30d&quot; CHECKSEQUENCEVERIFY DROP </span><br><span class="line">    &lt;Alice&#x27;s pubkey&gt; CHECKSIG</span><br><span class="line">ENDIF</span><br></pre></td></tr></table></figure><p>使用压缩公钥的话，那么上面脚本大小为150字节。采用MAST，可以把此脚本展开为两个分支：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">// 105字节</span><br><span class="line">2 &lt;Alice&#x27;s pubkey&gt; &lt;Bob&#x27;s pubkey&gt; &lt;Escrow&#x27;s pubkey&gt; 3 CHECKMULTISIGVERIFY</span><br><span class="line"></span><br><span class="line">// 42字节</span><br><span class="line">&quot;30d&quot; CHECKSEQUENCEVERIFY &lt;Alice&#x27;s pubkey&gt; CHECKSIGVERIFY </span><br></pre></td></tr></table></figure><h3 id="Hashed-Time-Lock-Contract"><a href="#Hashed-Time-Lock-Contract" class="headerlink" title="Hashed Time-Lock Contract"></a>Hashed Time-Lock Contract</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">HASH160 DUP &lt;R-HASH&gt; EQUAL</span><br><span class="line">IF</span><br><span class="line">    &quot;24h&quot; CHECKSEQUENCEVERIFY</span><br><span class="line">    2DROP</span><br><span class="line">    &lt;Alice&#x27;s pubkey&gt;</span><br><span class="line">ELSE</span><br><span class="line">    &lt;Commit-Revocation-Hash&gt; EQUAL</span><br><span class="line">    NOTIF</span><br><span class="line">        &quot;Timestamp&quot; CHECKLOCKTIMEVERIFY DROP</span><br><span class="line">    ENDIF</span><br><span class="line">    &lt;Bob&#x27;s pubkey&gt;</span><br><span class="line">ENDIF</span><br><span class="line">CHECKSIG</span><br></pre></td></tr></table></figure><p>采用MAST，展开为三个分支：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">// 1</span><br><span class="line">HASH160 &lt;R-HASH&gt; EQUALVERIFY“24h”CHECKSEQUENCEVERIFY &lt;Alice&#x27;s pubkey&gt; CHECKSIGVERIFY</span><br><span class="line"></span><br><span class="line">// 2</span><br><span class="line">HASH160 &lt;Commit-Revocation-Hash&gt; EQUALVERIFY &lt;Bob&#x27;s pubkey&gt; CHECKSIGVERIFY</span><br><span class="line"></span><br><span class="line">// 3</span><br><span class="line">&quot;Timestamp&quot; CHECKLOCKTIMEVERIFY &lt;Bob&#x27;s pubkey&gt; CHECKSIGVERIFY</span><br></pre></td></tr></table></figure><p>在提高可读性的同时，也可以显著减少witness体积。</p><h3 id="大型多重签名合约"><a href="#大型多重签名合约" class="headerlink" title="大型多重签名合约"></a>大型多重签名合约</h3><p>当前的多重签名最多允许20把公钥，通过<code>CHECKSIGs</code>和<code>IF/ELSE</code>组合一下，可以突破20把公钥限制，但不会突破太多数量，因为很快就会受到<code>10,000</code>字节和201个<code>sigops</code>限制。</p><p>通过MAST，可以轻松的构造出 3 of 2000的大型多重签名。从2000人随时抽取3个，那么组合数量为：(2000*1999*1998)&#x2F;(3*2*1)&#x3D;1,331,334,000。即1,331,334,000个3 of 3的CHECKMULTISIGVERIFY多签验证。即使这么多可能性分支，也不过就是31层的MAST，2^30 &lt; 1,331,334,000 &lt; 2^31。输出依然是34字节，花费时的witness也不会超过1500字节。</p><h3 id="非共识层面的强数据"><a href="#非共识层面的强数据" class="headerlink" title="非共识层面的强数据"></a>非共识层面的强数据</h3><p>当前，这些数据一般通过<code>OP_RETURN</code>进行提交，放到交易的输出中。例如存在证明，可以将某个哈希值通过放入交易的输出里，而提交至区块链存储。</p><p>通过MAST，可以包含进更多的数据，无需提交每一个值，将其作为MAST分支，那么永远只需要提交32字节即可。</p><hr><p>参考：</p><ul><li><a href="https://github.com/bitcoin/bips/blob/master/bip-0114.mediawiki">https://github.com/bitcoin/bips/blob/master/bip-0114.mediawiki</a></li><li><a href="https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki">https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki</a></li></ul>]]>
    </content>
    <id>https://kevinpan.page/2019/02/24/MAST/</id>
    <link href="https://kevinpan.page/2019/02/24/MAST/"/>
    <published>2019-02-24T06:40:02.000Z</published>
    <summary>
      <![CDATA[<h2 id="写在前面的话"><a href="#写在前面的话" class="headerlink" title="写在前面的话"></a>写在前面的话</h2><p>个人认为比特币脚本发展有三个阶段：</p>
<p>第一阶段，原始脚本阶段，仅有脚本。典型的有：P2PH, P]]>
    </summary>
    <title>MAST, Merkelized Abstract Syntax Tree</title>
    <updated>2019-02-24T06:40:02.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Kevin Pan</name>
      <email>bit.kevin@gmail.com</email>
    </author>
    <content>
      <![CDATA[<p>现有四种ECDSA签名的验证OP码: <code>OP_CHECKSIG</code>, <code>OP_CHECKSIGVERIFY</code>, <code>OP_CHECKMULTISIG</code>, <code>OP_CHECKMULTISIGVERIFY</code>。</p><p>还有四种签名的种类：<code>ALL</code>, <code>NONE</code>, <code>SINGLE</code>, <code>ANYONECANPAY</code>。</p><p>但是存在至少两个缺陷：</p><ul><li>现在的验证交易存在潜在过于复杂的问题，验证过程的时间度复杂是O(n^2)。构造一个1MB大小的交易，则需要花费2秒左右才能完成验证。</li><li>交易签名时，在不访问前向输出的情况下，是无法知道输入的金额的。因为交易输入里记录的前向交易的哈希值以及位于前向交易的第几个输出位置，其中并不包含金额。导致冷钱包或者分离式的签名器无法安全的进行签名操作，被迫信任传入的交易数据金额。</li></ul><p>在原有的脚本上解决这个问题，并非易事。不过，在Segwit软分叉基础上，可以做到。</p><h2 id="验证交易的时间复杂度缺陷"><a href="#验证交易的时间复杂度缺陷" class="headerlink" title="验证交易的时间复杂度缺陷"></a>验证交易的时间复杂度缺陷</h2><p>现有交易验证过程：</p><ol><li>剔除交易其他输入的签名数据</li><li>替换当前输入的脚本<code>scriptSig</code>为前向交易对应输出的脚本<code>scriptPubKey</code></li><li>对当前交易做哈希运算，double-SHA256后得到32字节的结果</li><li>对32字节结果进行签名</li><li>循环这个过程，直到遍历处理完所有输入</li></ol><p>为何上述过程的时间复杂度是O(n^2)呢？很容易，两个线性叠加了：1. 交易越大，输入数量越多 2. 交易越大，单次交易哈希计算过程越长。</p><p>构建一个典型的交易：</p><ul><li>一个1MB的交易，输入3,300个，输出406,000 bytes，仅哈希计算则需要：3300 * 406,000，约1.34GB，需要时间10.9秒。</li><li>一个8MB的交易，输入22,500个，输出3.95MB，仅哈希计算则需要：22500 * 3.95，大约是88.8GB，需要时间超过11分钟。</li></ul><p>也就是说随着交易体积的线性增大，验证交易的时间呈指数级增长。</p><h2 id="实施细节"><a href="#实施细节" class="headerlink" title="实施细节"></a>实施细节</h2><p>重新定义一个交易哈希摘要算法，但新摘要算法仅与version为零的witness program一起工作：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">Double SHA256一下字段序列化后的数据:</span><br><span class="line">     1. 交易版本号，nVersion  (4-byte little endian)</span><br><span class="line">     2. 所有前向交易输出点(hash &amp; position)哈希，hashPrevouts (32-byte hash)</span><br><span class="line">     3. 所有输入的序列值哈希，hashSequence (32-byte hash)</span><br><span class="line">     4. 当前输入的前向交易输出点，outpoint (32-byte hash + 4-byte little endian) </span><br><span class="line">     5. 当前输入的前向交易输出脚本，scriptCode of the input (serialized as scripts inside CTxOuts)</span><br><span class="line">     6. 当前输入的前向交易输出金额，value of the output spent by this input (8-byte little endian)</span><br><span class="line">     7. 当前输入的序列值，nSequence of the input (4-byte little endian)</span><br><span class="line">     8. 交易所有输出的哈希，hashOutputs (32-byte hash)</span><br><span class="line">     9. 锁定时间值，nLocktime of the transaction (4-byte little endian)</span><br><span class="line">    10. 签名哈希类型值，sighash type of the signature (4-byte little endian)</span><br></pre></td></tr></table></figure><p>若只有1，4，7，9，10，则与旧签名算法中的含义一致。</p><p>对于解决复杂度问题，新摘要算法的解决办法很简单，把重复计算的数据先预计算为32字节的哈希值，那么整个新算法的序列化后数据长度则与交易大小基本没关系。</p><p>其中5，6保证了签名时，签名者可确认签名金额和脚本，解决了输入金额盲签的问题。</p><hr><p>参考：</p><ul><li><a href="https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki">https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki</a></li></ul>]]>
    </content>
    <id>https://kevinpan.page/2019/02/21/bip143/</id>
    <link href="https://kevinpan.page/2019/02/21/bip143/"/>
    <published>2019-02-22T00:43:27.000Z</published>
    <summary>
      <![CDATA[<p>现有四种ECDSA签名的验证OP码: <code>OP_CHECKSIG</code>, <code>OP_CHECKSIGVERIFY</code>, <code>OP_CHECKMULTISIG</code>, <code>OP_CHECKMULTISIGVERIFY<]]>
    </summary>
    <title>bip143-新的交易签名摘要算法</title>
    <updated>2019-02-22T00:43:27.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Kevin Pan</name>
      <email>bit.kevin@gmail.com</email>
    </author>
    <content>
      <![CDATA[<h2 id="Transaction-ID"><a href="#Transaction-ID" class="headerlink" title="Transaction ID"></a>Transaction ID</h2><p>在新的结构下，每个交易将有两个ID.</p><p><code>txid</code>保持不变，Double SHA256哈希以下序列化的数据：</p><p><code>[nVersion][txins][txouts][nLockTime]</code></p><p>新定义一个<code>wtxid</code>，Double SHA256哈希含有witness数据的序列化数据：</p><p><code>[nVersion][marker][flag][txins][txouts][witness][nLockTime]</code></p><p>其中：</p><ul><li><code>nVersion</code>, <code>txins</code>, <code>txouts</code>, and <code>nLockTime</code>保持不变</li><li><code>marker</code>必须为一个字节的零值：<code>0x00</code></li><li><code>flag</code>必须为一个字节的非零值，目前使用<code>0x01</code></li><li><code>witness</code>为交易的所有见证数据<ul><li>起始用<code>var_int</code>表示有几个输入</li><li>然后每个项的起始用<code>var_int</code>标识其数据长度</li><li>Witness数据并不是脚本</li></ul></li></ul><p>如果一个交易输入不含有任何的witness program（可以视为不带OP操作码的<code>scriptPubKey</code>），则交易的<code>wtxid</code>等于<code>txid</code>。</p><h2 id="Commitment"><a href="#Commitment" class="headerlink" title="Commitment"></a>Commitment</h2><p>新增一个规则，coinbase交易的wtxid认为是<code>0x0000...0000</code>。</p><p>新增一个<code>witness root hash</code>，每个交易的<code>wtxid</code>作为叶子计算而来。类似原块头中的<code>merkle root hash</code>。</p><p>这个Commitment存在在coinbase交易的某个输出<code>scriptPubKey</code>中，长度必须至少为38Bytes，前六个字节内容固定是<code>0x6a24aa21a9ed</code>：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"> 1-byte - OP_RETURN (0x6a)</span><br><span class="line"> 1-byte - Push the following 36 bytes (0x24)</span><br><span class="line"> 4-byte - Commitment header (0xaa21a9ed)</span><br><span class="line">32-byte - Commitment hash: Double-SHA256(witness root hash|witness reserved value)</span><br><span class="line">  </span><br><span class="line">39th byte onwards: Optional data with no consensus meaning</span><br></pre></td></tr></table></figure><p>如果交易的多个输出符合六个字节前缀，则认为序列最大的输出项为Commitment。</p><p>Coinbase交易5b298d2e51d996cf6f8f5b12f2c44fa3203b39e0514025268b55ddc46ac72e73，其Hex内容为：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff4d03819a08045cda6c5c2f706f6f6c696e2e636f6d2ffabe6d6db24bb958ea51e4bb25e0975c5ff65071a38f81d6e65a447105e1340b24171bee01000000000000009e2268d82707000000000000ffffffff025cac6d4b0000000017a914b757c3e4653706dd01f7b8345a6d71d96a7b136b870000000000000000266a24aa21a9ed5586b81f398ff1a8f20da1405bc92e6bbd2dda466414ca90bbd8215f77fe0c340120000000000000000000000000000000000000000000000000000000000000000000000000</span><br></pre></td></tr></table></figure><p>分解Coinbase TX：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line">01000000 - version</span><br><span class="line">00 - marker</span><br><span class="line">01 - flag</span><br><span class="line"></span><br><span class="line">Inputs:</span><br><span class="line">01 - 1 input</span><br><span class="line">  input[0].prev-txout-hash 0000000000000000000000000000000000000000000000000000000000000000</span><br><span class="line">  input[0].prev-txout-position ffffffff</span><br><span class="line">  input[0].sigScript:</span><br><span class="line">  4d - len, 0x4d=77Bytes</span><br><span class="line">  03819a08045cda6c5c2f706f6f6c696e2e636f6d2ffabe6d6db24bb958ea51e4bb25</span><br><span class="line">  e0975c5ff65071a38f81d6e65a447105e1340b24171bee01000000000000009e2268</span><br><span class="line">  d82707000000000000</span><br><span class="line">  input[0].sequence: ffffffff</span><br><span class="line"></span><br><span class="line">Outputs:</span><br><span class="line">02 - 2 outputs</span><br><span class="line">  output[0].value - 5cac6d4b00000000</span><br><span class="line">  output[0].scriptPubKey - 17a914b757c3e4653706dd01f7b8345a6d71d96a7b136b87</span><br><span class="line">  </span><br><span class="line">  output[1].value - 0000000000000000</span><br><span class="line">  output[1].scriptPubKey -</span><br><span class="line">  266a24aa21a9ed5586b81f398ff1a8f20da1405bc92e6bbd2dda466414ca90bbd821</span><br><span class="line">  5f77fe0c34</span><br><span class="line"></span><br><span class="line">Witness:</span><br><span class="line">01 - 1 witness item</span><br><span class="line">  witness[0].len - 20</span><br><span class="line">  witness[0].data - </span><br><span class="line">  0000000000000000000000000000000000000000000000000000000000000000</span><br><span class="line"></span><br><span class="line">00000000 - locktime</span><br></pre></td></tr></table></figure><p>其实就是两套Merkle树，一套由<code>txid</code>作为叶子，一套由<code>wtxid</code>作为叶子。保持软分叉，块头不动，新的一套Merkle树根值，放入Coinbase交易里。Coinbase交易呢，走原来的Merkle树，根值放入块头。</p><h2 id="Witness-Program"><a href="#Witness-Program" class="headerlink" title="Witness Program"></a>Witness Program</h2><h3 id="Native-Witness-Program"><a href="#Native-Witness-Program" class="headerlink" title="Native Witness Program"></a>Native Witness Program</h3><p>此时输出的<code>scriptPubKey</code>内容为：</p><p><code>[version][witness_program]</code></p><ul><li><code>version</code> ：1 bytes，表示版本，值范围是：[0, 16]</li><li><code>witness_program</code>: 数据，长度2~40 Bytes</li></ul><p>当前，version是<code>0x00</code>，<code>witness_program</code>的长度为20或32字节。</p><p>比特币脚本中与version可能相关的OP操作编码：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">OP_0 = 0x00</span><br><span class="line"></span><br><span class="line">OP_1 = 0x51,  // Alias OP_TRUE</span><br><span class="line">OP_2 = 0x52,</span><br><span class="line">OP_3 = 0x53,</span><br><span class="line">OP_4 = 0x54,</span><br><span class="line">OP_5 = 0x55,</span><br><span class="line">OP_6 = 0x56,</span><br><span class="line">OP_7 = 0x57,</span><br><span class="line">OP_8 = 0x58,</span><br><span class="line">OP_9 = 0x59,</span><br><span class="line">OP_10 = 0x5a,</span><br><span class="line">OP_11 = 0x5b,</span><br><span class="line">OP_12 = 0x5c,</span><br><span class="line">OP_13 = 0x5d,</span><br><span class="line">OP_14 = 0x5e,</span><br><span class="line">OP_15 = 0x5f,</span><br><span class="line">OP_16 = 0x60</span><br></pre></td></tr></table></figure><h3 id="P2SH-Witness-Program"><a href="#P2SH-Witness-Program" class="headerlink" title="P2SH Witness Program"></a>P2SH Witness Program</h3><p>即通过P2SH进行包装一下：</p><p><code>OP_HASH160 [20 BYTES HASH] OP_EQUAL</code></p><p>其中：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">20_BYTES_HASH = HASH160(redeemScript)</span><br><span class="line"></span><br><span class="line">redeemScript = [version] + [witness_program]</span><br></pre></td></tr></table></figure><h4 id="P2WPKH"><a href="#P2WPKH" class="headerlink" title="P2WPKH"></a>P2WPKH</h4><p>P2WPKH，即Pay to Witness Pubkey Hash。Witness Program长度为20字节。</p><p>其中：</p><ul><li>version &#x3D; 0</li><li>witness_program &#x3D; hash160(pubkey)</li></ul><p>脚本堆栈为：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">Witness:</span><br><span class="line">[signature] [pubkey]</span><br><span class="line"></span><br><span class="line">scriptSig:</span><br><span class="line">(empty)</span><br><span class="line"></span><br><span class="line">scriptPubKey:</span><br><span class="line">[0] [20_bytes]</span><br></pre></td></tr></table></figure><p>通过执行<code>CHECKSIG</code>来完成签名验证：<code>&lt;signature&gt; &lt;pubkey&gt; CHECKSIG</code>。在验证脚本时，会先封装20bytes的数据为脚本：<code>OP_HASH160 &lt;20bytes&gt; OP_EQUAL</code>，并入栈执行。</p><p>若采用P2SH来包装的话，则有：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">     witness: &lt;signature&gt; &lt;pubkey&gt;</span><br><span class="line">   scriptSig: &lt;redeem_script&gt;</span><br><span class="line">scriptPubKey: HASH160 &lt;20-byte-script-hash&gt; EQUAL</span><br><span class="line"></span><br><span class="line">redeem_script : [version] + [witness_program]</span><br></pre></td></tr></table></figure><p>会先验证redeem_script的hash160，然后再分解redeem_script得到20Bytes，再包装为<code>OP_HASH160 &lt;20bytes&gt; OP_EQUAL</code>，并入栈执行。</p><h4 id="P2WSH"><a href="#P2WSH" class="headerlink" title="P2WSH"></a>P2WSH</h4><p>P2WSH，即Pay to Witness Script Hash。Witness Program长度为32字节。</p><p>其中：</p><ul><li>version &#x3D; 0</li><li>witness_program &#x3D; sha256(witnessScript)</li></ul><p>脚本堆栈为（1&#x2F;2多重签名）：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">Witness:</span><br><span class="line">0 &lt;signature1&gt; &lt;witness_script&gt;</span><br><span class="line"></span><br><span class="line">scriptSig:</span><br><span class="line">(empty)</span><br><span class="line"></span><br><span class="line">scriptPubKey:</span><br><span class="line">[0] [32_bytes]</span><br><span class="line"></span><br><span class="line">其中：</span><br><span class="line">witness_script = 1 &lt;pubkey1&gt; &lt;pubkey2&gt; 2 CHECKMULTISIG</span><br></pre></td></tr></table></figure><p>执行与P2SH的redeem_script一致，分为两步：</p><ol><li>验证witness_script的sha256哈希值。</li><li>分解witness_script为子脚本，重新入栈并执行脚本。</li></ol><h2 id="其他共识规则"><a href="#其他共识规则" class="headerlink" title="其他共识规则"></a>其他共识规则</h2><h3 id="Block-size-区块大小"><a href="#Block-size-区块大小" class="headerlink" title="Block size 区块大小"></a>Block size 区块大小</h3><p>原来的规则是单个区块大小最大值为1,000,000 Bytes，因剥离出Witness数据，这里提出一个新的单位Weight进行改进：</p><p><code>Block_Weight = Base_Size * 3 + Total_Size</code></p><ul><li><code>Base_Size</code>，基础大小，定义是：剥离witness相关数据后的大小，即未升级节点所看到的数据部分。</li><li><code>Total_Size</code>，全大小，定义是：包含witness相关数据的所有数据大小。</li><li><code>Base_Size &lt;= 4,000,000</code></li></ul><h3 id="Sigops-操作数"><a href="#Sigops-操作数" class="headerlink" title="Sigops 操作数"></a>Sigops 操作数</h3><p>Sigops当前单个区块限制是 20,000，修改为：</p><p>用于pubkey script, signature script, 以及 P2SH check script的Sigops重新定义为原来的4倍。块限制亦同样的变为四倍 80,000.</p><h2 id="Commitment结构可扩展性"><a href="#Commitment结构可扩展性" class="headerlink" title="Commitment结构可扩展性"></a>Commitment结构可扩展性</h2><p>现有commitment计算过程：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Commitment hash: Double-SHA256(witness root hash|witness reserved value)</span><br><span class="line"></span><br><span class="line">witness reserved value: 当前是32 bytes的零值，存在在coinbase交易的witness里</span><br></pre></td></tr></table></figure><p>例如我们新加了一个特性，其也有数据的commitment值，那么可以将原来的<code>witness reserved value</code>更新为：<code>Hash(new commitment|witness reserved value)</code>，那么并不影响旧节点对commitment的校验。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Double-SHA256(Witness root hash|Hash(new commitment|witness reserved value))</span><br></pre></td></tr></table></figure><p>这个方法可以扩展为多个commitment，未来可以用于其他的软分叉升级。</p><h2 id="未来的扩展"><a href="#未来的扩展" class="headerlink" title="未来的扩展"></a>未来的扩展</h2><h3 id="减少SPV欺骗"><a href="#减少SPV欺骗" class="headerlink" title="减少SPV欺骗"></a>减少SPV欺骗</h3><p>SPV节点目前依然有几个问题无法验证：</p><ul><li>无法验证矿工是否在coinbase交易里增发币。不下载完整区块，就无法的得知所有交易的手续费，就无法得知块手续费。</li><li>无法验证是否违反其他共识规则，例如块大小限制、sigops限制。</li><li>无法验证输入交易是否存在，除非拿到直至coinbase交易的前向交易链条。</li></ul><p>额外的数据可以采用commitment方式填入，借助软分叉升级。</p><h3 id="新的脚本系统"><a href="#新的脚本系统" class="headerlink" title="新的脚本系统"></a>新的脚本系统</h3><p><code>scriptPubKey</code>采用的版本号，若老节点看不懂这个新版本号，但会认为是任何人都可以花费的(因为脚本堆栈执行总是True)，所以将来可以采用这种机制来新增脚本系统，并且都将是软分叉。</p><p>而<code>witness</code>部分目前是没有任何限制的，也就是说可以完全绕开520字节的脚本大小限制。未来也可以轻松引入新的签名机制，例如Schnorr签名。</p><hr><p>参考：</p><ul><li><a href="https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki">https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki</a></li></ul>]]>
    </content>
    <id>https://kevinpan.page/2019/02/21/BIP141/</id>
    <link href="https://kevinpan.page/2019/02/21/BIP141/"/>
    <published>2019-02-22T00:34:02.000Z</published>
    <summary>
      <![CDATA[<h2 id="Transaction-ID"><a href="#Transaction-ID" class="headerlink" title="Transaction ID"></a>Transaction ID</h2><p>在新的结构下，每个交易将有两个ID.</p>]]>
    </summary>
    <title>BIP141, Segregated Witness</title>
    <updated>2019-02-22T00:34:02.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Kevin Pan</name>
      <email>bit.kevin@gmail.com</email>
    </author>
    <content>
      <![CDATA[<p>P2SH即Pay to Script Key Hash，最常见的应用是多重签名，N把公钥，M人签名时才能花费这笔交易(M&lt;&#x3D;N, N&lt;&#x3D;16)，地址通常是以3开头的格式。当然，p2sh除了用来做多重签名之外，能做的事情简直无数。</p><h2 id="P2PK"><a href="#P2PK" class="headerlink" title="P2PK"></a>P2PK</h2><p>比特币早期中本聪时期是P2PK(pay to public key)这种输出形式的，最早是直接放入一把公钥进去了，还是未压缩的，感受一下：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f OP_CHECKSIG</span><br></pre></td></tr></table></figure><ul><li>0x04开头，表示是未压缩的</li><li>0x04后面紧接这的是64字节是公钥内容</li><li>OP_CHECKSIG：操作码，用于花费的时执行验证签名</li></ul><p>那么验证时（即花费这笔输出）堆栈内容为：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">花费脚本为：</span><br><span class="line">&lt;signature&gt;</span><br><span class="line"></span><br><span class="line">连接前向的输出，完整的堆栈：</span><br><span class="line">&lt;signature&gt; &lt;pubkey&gt; OP_CHECKSIG</span><br></pre></td></tr></table></figure><p>OP_CHECKSIG执行签名检查，并返回检查结果，true则通过签名检查，可以花费。</p><h2 id="P2PKH"><a href="#P2PKH" class="headerlink" title="P2PKH"></a>P2PKH</h2><p>后来发现公钥其实用哈希就可以了，没必要放出公钥内容，被称为P2PKH(pay to public key hash)。于是输出脚本演化为：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">OP_DUP OP_HASH160 &lt;public_key_hash&gt; OP_EQUALVERIFY OP_CHECKSIG</span><br></pre></td></tr></table></figure><ul><li>OP_DUP: 操作码，复制栈顶元素</li><li>OP_HASH160：操作码，计算栈顶元素Hash，即计算公钥的哈希</li><li><code>public_key_hash</code>：公钥的哈希值，20字节</li><li>OP_EQUALVERIFY：操作码，判断是否相等</li><li>OP_CHECKSIG：操作码，用于花费的时执行验证签名</li></ul><p>此类型输出，就是最常见的1开头的地址输出。那么验证时的堆栈内容为：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">花费脚本为：</span><br><span class="line">&lt;signature&gt; &lt;pubkey&gt; </span><br><span class="line"></span><br><span class="line">连接前向的输出，完整的堆栈：</span><br><span class="line">&lt;signature&gt; &lt;pubkey&gt; OP_DUP OP_HASH160 &lt;pubkey_hash&gt; OP_EQUALVERIFY OP_CHECKSIG</span><br></pre></td></tr></table></figure><p>验证运算过程：</p><ol><li>从前往后找OP操作码，首先遇到<code>OP_DUP</code><ul><li>执行后堆栈为：<code>&lt;signature&gt; &lt;pubkey&gt; &lt;pubkey&gt; OP_HASH160 &lt;pubkey_hash&gt; OP_EQUALVERIFY OP_CHECKSIG</code></li></ul></li><li>执行<code>OP_HASH160</code><ul><li>执行后堆栈为：<code>&lt;signature&gt; &lt;pubkey&gt; &lt;pubkey_hash&gt; &lt;pubkey_hash&gt; OP_EQUALVERIFY OP_CHECKSIG</code></li></ul></li><li>执行<code>OP_EQUALVERIFY</code>，即验证该哈希是否与公钥匹配<ul><li>执行后堆栈为：<code>&lt;signature&gt; &lt;pubkey&gt; OP_CHECKSIG</code></li><li>到这里就与P2PKH一致了</li></ul></li><li>执行<code>OP_CHECKSIG</code>，验证签名。</li></ol><p>这个过程汪海波写过文章详细的阐述过：<a href="https://kevinpan.page/2016/04/08/understand-bitcoin-script/">理解比特币脚本</a>。p2pk改进为p2pkh后，输出长度缩小了一半多，同时隐私方面迈出了一小步：别人转给你的币在你未花费之前，别人是不知道你的公钥具体内容的。是不是很赞？</p><h2 id="原始多重签名"><a href="#原始多重签名" class="headerlink" title="原始多重签名"></a>原始多重签名</h2><p>随着社区快速发展，人们发现需要多重签名，很快就出现了多重签名的输出格式，Gavin在BIP11里描述了这种输出：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">m &#123;pubkey&#125;...&#123;pubkey&#125; n OP_CHECKMULTISIG</span><br></pre></td></tr></table></figure><p>2&#x2F;2的一个原始多重签名示例：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">2</span><br><span class="line">04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4</span><br><span class="line">0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af</span><br><span class="line">2 OP_CHECKMULTISIG</span><br></pre></td></tr></table></figure><p>验证时的堆栈内容为：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">花费脚本为：</span><br><span class="line">0 &lt;sig_1&gt; &lt;...&gt; &lt;sig_M&gt;</span><br><span class="line"></span><br><span class="line">连接前向的输出，完整的堆栈：</span><br><span class="line">0 &lt;sig_1&gt; &lt;...&gt; &lt;sig_M&gt; M &lt;pubkey_1&gt; &lt;...&gt; &lt;pubkey_N&gt; N OP_CHECKMULTISIG</span><br></pre></td></tr></table></figure><p>OP_CHECKMULTISIG的运行过程稍微复杂一些：</p><ol><li>弹出最后一个数，就是N，公钥总数。<ul><li>执行后堆栈为：<code>0 &lt;sig_1&gt; &lt;...&gt; &lt;sig_M&gt; M &lt;pubkey_1&gt; &lt;...&gt; &lt;pubkey_N&gt;</code></li></ul></li><li>弹出N个堆栈元素，就是这N把公钥。<ul><li>执行后堆栈为：<code>0 &lt;sig_1&gt; &lt;...&gt; &lt;sig_M&gt; M</code></li></ul></li><li>弹出签名数量M，即需要M个签名数量。<ul><li>执行后堆栈为：<code>0 &lt;sig_1&gt; &lt;...&gt; &lt;sig_M&gt;</code></li></ul></li><li>弹出M个堆栈元素，即需要M个签名。同时对M个签名进行验证。<ul><li>执行后堆栈为：<code>0</code></li></ul></li><li>弹出最后一个元素<code>0</code>。<ul><li><code>0</code>即<code>OP_0</code>，为啥多这么一个奇怪的元素呢，因为早期实现<code>OP_CHECKMULTISIG</code>时，存在一个BUG，导致必须多放入一个元素到堆栈里。为了保持兼容性，则不得不放入<code>OP_0</code>，否则就是造成硬分叉。</li></ul></li></ol><p>这种类型的输出存在时间很短，大部分人几乎不知道它的的存在，如果你哪天看见某个交易的一个输出里冒出好几个地址，那就是这种古老原始的多重签名。早期主要应用于2&#x2F;3的担保交易中。</p><h2 id="P2SH"><a href="#P2SH" class="headerlink" title="P2SH"></a>P2SH</h2><p>因为实在是又丑又笨，Gavin很快捣鼓出一个改进版本BIP16：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">OP_HASH160 &lt;redeem_script_hash&gt; OP_EQUAL</span><br></pre></td></tr></table></figure><p>其实不用把公钥放在输出里了，放入其HASH值即可，与早期P2PK进化为P2PKH一样，将这些公钥连接在一起并计算出其HASH160的值：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">RedeemScript = OP_nRequired | PUBKEY_1 | ... | PUBKEY_N | N | OP_CHECKMULTISIG</span><br><span class="line"></span><br><span class="line">20-byte-hash-value = RIPEMD-160(RedeemScript)</span><br></pre></td></tr></table></figure><p>RedeemScript就是把参与的公钥以及m&#x2F;n的设置值等连接在一起的内容，RedeemScript其实就是前面提到的“原始多重签名”的输出，哈希后产生的这20个字节刚好可以转为普通地址显示，就是现在最常见的3开头的p2sh多重签名地址。N最大为16把公钥。</p><p>验证时的堆栈内容为：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">花费脚本为：</span><br><span class="line">OP_0 &lt;sig_1&gt; &lt;...&gt; &lt;sig_M&gt; &lt;redeemScript&gt;</span><br><span class="line"></span><br><span class="line">连接前向的输出，完整的堆栈：</span><br><span class="line">OP_0 &lt;sig_1&gt; &lt;...&gt; &lt;sig_M&gt; &lt;redeemScript&gt; OP_HASH160 &lt;redeemScriptHash&gt; OP_EQUAL</span><br></pre></td></tr></table></figure><p>验证过程分为两大步骤，第一步骤：</p><ol><li>执行<code>OP_HASH160</code>，计算HASH值：<code>&lt;redeemScript&gt; OP_HASH160</code><ul><li>执行后堆栈为：<code>OP_0 &lt;sig_1&gt; &lt;...&gt; &lt;sig_M&gt; &lt;redeemScriptHash&gt;&lt;redeemScriptHash&gt; OP_EQUAL</code></li></ul></li><li>执行<code>OP_EQUAL</code>，验证两个哈希值是否相等<ul><li>执行后堆栈为：<code>OP_0 &lt;sig_1&gt; &lt;...&gt; &lt;sig_M&gt;</code></li></ul></li></ol><p>第二步骤，将展开花费脚本中的RedeemScript展开得到子脚本，就得到与“原始多重签名”一致的堆栈数据格式，并执行类似的验证过程：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">OP_0 &lt;sig_1&gt; &lt;...&gt; &lt;sig_M&gt; M &lt;pubkey_1&gt; &lt;...&gt; &lt;pubkey_N&gt; N OP_CHECKMULTISIG</span><br></pre></td></tr></table></figure><p>当然，RedeemScript除了放多重签名脚本外，还可以放其他任何脚本，多重签名仅仅是一种应用而已，p2sh提供了无限可能性。</p><h3 id="软分叉的关键点"><a href="#软分叉的关键点" class="headerlink" title="软分叉的关键点"></a>软分叉的关键点</h3><p>在第一步骤中，redeemScript是作为一个整体数据进栈的，而在第二步里，redeemScript会按照脚本进行解析得到N个栈元素，然后再依次进栈进行验证。这个过程是非常巧妙的，在第一步里，仅验证了脚本的哈希值是否一致，并没有验证签名。真正的签名信息是在第二步骤里进行验证的。因为这点，所以可以软分叉实施P2SH，老节点仅执行第一步骤，新节点执行两个步骤。</p><p>当花费过一次后，redeemScript其实就公开了，那么对于老节点来说，任何人都可以用这个公开的redeemScript花掉相同地址的币（验证哈希值）。这就是被称为任何人可以花费（Anyone can spend）的原因。不过，特性激活后，新版全节点（出块节点必然是新版）会强制执行第二步验证，永远都不会被其他人偷花。</p><h3 id="激活"><a href="#激活" class="headerlink" title="激活"></a>激活</h3><p>P2SH的激活，其实算是UASF或者是GASF（Gavin Actived Soft Fork），那时也没有规范的软分叉升级方案，如BIP9。升级代码就直接发布了，并设立了激活信号日2012年04月01日(测试网是2月15日)。支持的用户直接升级代码，不支持的用户不升级代码。</p><p>为了防止潜在网络分叉，矿工在coinbase交易里打标识<code>/P2SH/</code>来标明支持P2SH。但这个仅供人为观察，并不是代码层面的。</p><h3 id="影响深远"><a href="#影响深远" class="headerlink" title="影响深远"></a>影响深远</h3><p>P2SH是一个高度灵活的脚本方案，意义重大，影响深远，简直像打开了宝藏一样。其为后面的SegWit，MAST都铺平了道路。</p><h3 id="发展状况"><a href="#发展状况" class="headerlink" title="发展状况"></a>发展状况</h3><p>数据来源：<a href="https://p2sh.info/">https://p2sh.info</a></p><p>截止2019年2月，全网大约32%的币存储在P2SH的输出里。</p><p><img src="https://user-images.githubusercontent.com/514951/53393132-7ded5080-39d6-11e9-8dc9-9a859be381d2.png" alt="p2sh-values"></p><p>P2SH的输出类型统计：</p><p><img src="https://user-images.githubusercontent.com/514951/53393138-834a9b00-39d6-11e9-895c-3c0d2aa1abf4.png" alt="p2sh-types"></p><hr><p>参考</p><ul><li><a href="https://github.com/bitcoin/bips/blob/master/bip-0011.mediawiki">https://github.com/bitcoin/bips/blob/master/bip-0011.mediawiki</a></li><li><a href="https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki">https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki</a></li><li><a href="https://github.com/bitcoin/bitcoin/blob/v0.6.0/src/main.cpp#L1281">https://github.com/bitcoin/bitcoin/blob/v0.6.0/src/main.cpp#L1281</a></li></ul>]]>
    </content>
    <id>https://kevinpan.page/2019/02/12/p2sh/</id>
    <link href="https://kevinpan.page/2019/02/12/p2sh/"/>
    <published>2019-02-12T23:25:36.000Z</published>
    <summary>
      <![CDATA[<p>P2SH即Pay to Script Key Hash，最常见的应用是多重签名，N把公钥，M人签名时才能花费这笔交易(M&lt;&#x3D;N, N&lt;&#x3D;16)，地址通常是以3开头的格式。当然，p2sh除了用来做多重签名之外，能做的事情简直无数。</p>
<h]]>
    </summary>
    <title>P2SH-为比特币赋能的脚本</title>
    <updated>2019-02-12T23:25:36.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Kevin Pan</name>
      <email>bit.kevin@gmail.com</email>
    </author>
    <content>
      <![CDATA[<blockquote><p>版权声明：欢迎转载，请注明出处</p></blockquote><p>之前说过软分叉升级规范BIP9，BIP9可以说是经过良好设计的软分叉升级方案，可同时进行多个分叉的升级并且流程也很科学合理。</p><h2 id="BIP9"><a href="#BIP9" class="headerlink" title="BIP9"></a>BIP9</h2><p>简单回顾一下BIP9的主要特性：</p><ol><li>95%的高投票阈值，块投票必须达到95%以上的支持率才能进入锁定期，然后触发激活。</li><li>设定了投票时间窗口，有起始、终止时间，在窗口期内未激活的软分叉只能终止或者重新设定发起新一轮投票。</li><li>块时间采用相邻11个块的块中位数时间。</li></ol><p>BIP9截止到当前（2019年02月）共使用过两次：</p><ol><li>CSV（BIP68, BIP112, and BIP113）</li><li>SegWit（BIP141, BIP143, and BIP147）</li></ol><p>在CSV软分叉中进展一切正常，但SegWit软分叉中却在投票阶段延迟了大半年，块投票一直无法达到95%的激活阈值，以至于后来出现了BIP148（即UASF，user-activated soft fork）和BIP91。SegWit在BIP9中设定的时间区间是：2016年11月15日~2017年11月15日。</p><p>这里我们稍微说一下BIP148和BIP91，只有理解了这个过程才能理解为何会有BIP8（这些事情其实已经发生挺久了，2017年中，快两年前的事情了）。值得一提的是，BIP148和BIP91的代码，从来没有进入Bitcoin Core版本的代码库。</p><h2 id="BIP148"><a href="#BIP148" class="headerlink" title="BIP148"></a>BIP148</h2><p>BIP148，更熟悉的名称是UASF，目的是强制性进行激活SegWit：若块时间处于2017年08月01日与2017年11月15日，且SegWit尚未激活或没有进入锁定期，则直接拒绝不支持SegWit投票的块。BIP148通过用户自行更新代码，下载Bitcoin Core的代码然后打补丁的方式，来声明和实施。</p><p>UASF是非常激进的升级方式，直接抛开了矿工算力的投票，不管算力是否投票支持SegWit，这些UASF全节点到时候会直接抛弃掉非SegWit的块。这在当时还非常认可矿工和算力的时代，对于矿工和算力来说简直是晴天霹雳：直接撇开了！</p><p>当然，很多交易所对这一事件进行了预演：假设分叉会发生，并上市了推演分叉后的两个币种期货，把价格交给市场去判断。</p><h2 id="BIP91"><a href="#BIP91" class="headerlink" title="BIP91"></a>BIP91</h2><p>BIP91，目的是也是激活SegWit，通过降低SegWit激活阈值至80%来间接完成，其自身采用类似BIP9的方式进行部署：</p><ol><li>激活时间区间是：2017年06月1日 ~ 2017年11月15日。</li><li>块时间窗口非常短，不再是BIP9中的2016个块，而是336个块，大约2.33天。</li><li>激活阈值为80%，不再是BIP9的95%。</li></ol><p>BIP148(UASF)直接撇开矿工由全节点直接激活，而BIP91依然把选择权给了矿工：通过块投票进行激活。最终，由UASF主导的强大社区压力下，BIP91很快通过80%的块投票阈值进入锁定并激活了。BIP91一旦激活，则意味着后面的块必须进行支持SegWit投票，间接促成了SegWit通过95%的块投票阈值并锁定激活了。</p><p>以上即是SegWit激活受阻大半年后，UASF&amp;BIP91间接促成SegWit激活的历史过程。</p><h2 id="BIP8"><a href="#BIP8" class="headerlink" title="BIP8"></a>BIP8</h2><p>BIP8是在BIP9基础之上的改进：</p><ol><li>采用更加精确的块高度窗口代替块时间窗口，消除了块时间的不稳定性。</li><li>统计周期依然是与BIP9一致的2016个块。</li><li>几乎不再有失败的状态，除非编码之初设定的高度已经是过去高度。</li><li>设立激活起始块高度，一旦当前高度大于起始块高度，则开始计算是否激活。起始块高度必须超过当前高度4320个块，约30天。</li><li>设立截止块高度，无论投票是否通过，都在截止块高度达到时进行强制激活。截止高度通常是起始高度的52416块之后，约一年。</li><li>在抵达截止块高度前，若投票超过阈值，阈值与BIP9一致为95%，则提前进入锁定期并随之激活。</li></ol><p><img src="https://github.com/bitcoin/bips/raw/master/bip-0008/states.png" alt="BIP8 States"></p><p>总结，如果某个软分叉遵循BIP8激活机制的话，一旦部署了，那么矿工可以进行投票提前激活，或者在一年后的截止高度抵达时自动激活。</p><h3 id="BIP8的主要意义"><a href="#BIP8的主要意义" class="headerlink" title="BIP8的主要意义"></a>BIP8的主要意义</h3><ol><li>取消了矿工的否决权：要么投票主动提前激活，要么不投票被动等抵达截止高度自动激活。<ul><li>Vote转变成Signal。</li><li>算力大小是价格高低的结果，严格来说是链的法币日产量决定算力规模。</li><li>价格由市场供需决定，体现为共识主导价格。</li></ul></li><li>社区的决策机制发生了根本性改变：从小圈子投票变成了一定程度上的普选。<ul><li>无论是算力投票还是持币投票，由于存在中间代理层（例如矿池、交易所、钱包等），均无法避免最终沦为一定程度上的小圈子投票。</li><li>一个全节点即一张选票，全节点构成最广泛的共识。</li><li>全节点一方面制约着算力，另一方面也制约代码。无论是矿霸还是码霸，均无法强制约束全节点的行为。</li></ul></li></ol><h2 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h2><p>从中本聪白皮书时代以来，大多数人坚信着“One CPU One Vote”的民主观念，在经历了2017的SegWit激活历程后，其中还硬分叉诞生了Bitcoin Cash（BCH），终于得到脱胎换骨的革新。大家发现只有全节点才是最终的堡垒和武器，运行全节点是平等自由的互联网所赋予的谁也剥夺不了的权利。</p><hr><p>参考</p><ul><li>BIPs<ul><li><a href="https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki">https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki</a></li><li><a href="https://github.com/bitcoin/bips/blob/master/bip-0008.mediawiki">https://github.com/bitcoin/bips/blob/master/bip-0008.mediawiki</a></li><li><a href="https://github.com/bitcoin/bips/blob/master/bip-0148.mediawiki">https://github.com/bitcoin/bips/blob/master/bip-0148.mediawiki</a></li><li><a href="https://github.com/bitcoin/bips/blob/master/bip-0091.mediawiki">https://github.com/bitcoin/bips/blob/master/bip-0091.mediawiki</a></li></ul></li></ul>]]>
    </content>
    <id>https://kevinpan.page/2019/02/11/bip8/</id>
    <link href="https://kevinpan.page/2019/02/11/bip8/"/>
    <published>2019-02-12T04:51:05.000Z</published>
    <summary>
      <![CDATA[<blockquote>
<p>版权声明：欢迎转载，请注明出处</p>
</blockquote>
<p>之前说过软分叉升级规范BIP9，BIP9可以说是经过良好设计的软分叉升级方案，可同时进行多个分叉的升级并且流程也很科学合理。</p>
<h2 id="BIP9"><a hre]]>
    </summary>
    <title>谈谈软分叉升级规范BIP8-后矿工时代的软分叉方式</title>
    <updated>2019-02-12T04:51:05.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Kevin Pan</name>
      <email>bit.kevin@gmail.com</email>
    </author>
    <content>
      <![CDATA[<p>这是在2018年3月在三里屯WeWork一次分享，主要讲了闪电网络的基础知识，以及扩容的发展历程。</p><p>视频地址： <a href="https://www.yizhibo.com/l/jW2SzxIVFOzKF4k_.html">https://www.yizhibo.com/l/jW2SzxIVFOzKF4k_.html</a><br>PDF地址：<a href="https://github.com/bitkevin/blog.biqu.io/files/1845471/default.pdf">https://github.com/bitkevin/blog.biqu.io/files/1845471/default.pdf</a></p>]]>
    </content>
    <id>https://kevinpan.page/2018/03/25/The-Significance-of-the-Lightning-Network/</id>
    <link href="https://kevinpan.page/2018/03/25/The-Significance-of-the-Lightning-Network/"/>
    <published>2018-03-26T04:25:45.000Z</published>
    <summary>
      <![CDATA[<p>这是在2018年3月在三里屯WeWork一次分享，主要讲了闪电网络的基础知识，以及扩容的发展历程。</p>
<p>视频地址： <a href="https://www.yizhibo.com/l/jW2SzxIVFOzKF4k_.html">https://www.yizh]]>
    </summary>
    <title>闪电网络的伟大意义 The Significance of the Lightning Network</title>
    <updated>2018-03-26T04:25:45.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Kevin Pan</name>
      <email>bit.kevin@gmail.com</email>
    </author>
    <content>
      <![CDATA[<p>比特币的软&#x2F;硬分叉升级一直采用块的<code>version</code>字段来完成。由于是一个分布式系统，必然需要采用灰度发布模式。</p><h3 id="传统的升级过程"><a href="#传统的升级过程" class="headerlink" title="传统的升级过程"></a>传统的升级过程</h3><p>在实施<a href="https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki">BIP9</a>前是这样升级的，当前块版本为<code>version</code>，那么新块版本是<code>version + 1</code>，当近1000个块中的版本超过95%都是新版本时，则触发启用新特性，同时不再接收旧版本号的块。由于中间存在1000个块的窗口期，大约一周，所以给出足够的时间给当前网络中的节点实施升级。</p><p><img src="https://cloud.githubusercontent.com/assets/514951/14696861/4a75b116-07b1-11e6-99da-0b28e3408614.png" alt="图1.0"></p><p>上图是来自<a href="https://chain.btc.com/stats/block-ver?history_mode=all#versionTrend">BTC.COM</a>统计的比特币历史上几个升级过程，最近的v3升级v4的过程大约花费了一个半月左右。前面几次的升级时间更长。</p><p>这种依次递增版本号的方法，有一个明显的弊端：每次仅能进行一个特性升级。当需要同时进行多个升级时，则无法完成。BIP9的诞生就是为了解决这个问题的，同时把向下兼容性升级过程制定了规范。</p><h3 id="BIP9特征"><a href="#BIP9特征" class="headerlink" title="BIP9特征"></a>BIP9特征</h3><ol><li>支持多个特性同时升级</li><li>新增投票时间区间</li><li>新增锁定期</li></ol><p>块的<code>version</code>字段是4字节，意味着有32位比特，前三位目前固定为<code>001</code>，那么剩余29个比特可以用于特性设置，与激发某个特性，则将其对应的位设置为<code>1</code>。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">二进制格式：</span><br><span class="line"></span><br><span class="line">   001   0 0000 0000 0000 0000 0000 0000 0000</span><br><span class="line">------   ------------------------------------</span><br><span class="line">固定保留  29个特性标识位</span><br></pre></td></tr></table></figure><h3 id="状态变迁"><a href="#状态变迁" class="headerlink" title="状态变迁"></a>状态变迁</h3><p>BIP9某个特性的状态有：</p><ul><li>DEFINED <code>定义</code>。每个特性的升级设定会写到某一个版本的bitcoind中，并部署下去。</li><li>STARTED <code>启动</code>。启动后会有两个可能的变迁状态：<code>LOCKED_IN</code>, <code>FAILED</code>。</li><li>LOCKED_IN <code>锁定</code>。一旦进入锁定态，则必然会激活。锁定期为2016个块。</li><li>ACTIVE <code>激活</code></li><li>FAILED <code>失败</code></li></ul><p><img src="https://cloud.githubusercontent.com/assets/514951/14697672/a19038d4-07b8-11e6-9101-a20b009e102f.png" alt="图1.1"></p><p><code>MTP</code>是最近11个块的时间戳中位数。区块链上的块时间并不是按照高度递增而严格递增的，但<code>MTP</code>时间是严格递增的(早期可能不能严格遵守)。</p><p>能够进入锁定期（即得到全网同意）的条件是在2016个块中，支持的块数量达到95%，在测试网络上是75%。窗口期从之前的1000提升至2016个块，即一个难度周期，约两周。同时，还增加了2016个块的锁定期，之后才能正式激活特性。这比之前的升级，变得更加稳健，预留出充分的时间，同时意味着特性的落地周期变长。</p><h3 id="首个采用BIP9的软分叉"><a href="#首个采用BIP9的软分叉" class="headerlink" title="首个采用BIP9的软分叉"></a>首个采用BIP9的软分叉</h3><p><code>BIP68/112/113</code>是首个采用BIP9方式进行升级的软分叉，随<code>bitcoind-v0.12.1</code>发布。使能位采用最右侧位，起始时间(<em>starttime</em>)是<code>2016-05-01 00:00:00</code>，终止时间(<em>timeout</em>)是<code>2017-05-01 00:00:00</code>。</p><p>若要支持<code>BIP68/112/113</code>软分叉，那么版本号应该是: <code>0x20000001</code>，二进制为：<code>0010 0000 0000 0000 0000 0000 0000 0001</code>。</p><p>另外<code>BIP109</code>(Classic 2MB HardFork)也是采用的是BIP9规范，使能位是最左侧位，但触发窗口期与锁定期的设定均未采用BIP9的定义。其版本号是：<code>0x30000000</code>，二进制为：<code>0011 0000 0000 0000 0000 0000 0000 0000</code>。</p><p>如果版本号为：<code>0x30000001</code>，二进制为：<code>0011 0000 0000 0000 0000 0000 0000 0001</code>，则意味着同时支持<code>BIP68/112/113</code>和<code>BIP109</code>。</p>]]>
    </content>
    <id>https://kevinpan.page/2016/04/21/BIP9/</id>
    <link href="https://kevinpan.page/2016/04/21/BIP9/"/>
    <published>2016-04-21T18:09:52.000Z</published>
    <summary>
      <![CDATA[<p>比特币的软&#x2F;硬分叉升级一直采用块的<code>version</code>字段来完成。由于是一个分布式系统，必然需要采用灰度发布模式。</p>
<h3 id="传统的升级过程"><a href="#传统的升级过程" class="headerlink" title]]>
    </summary>
    <title>新的软分叉升级规范BIP9</title>
    <updated>2016-04-21T18:09:52.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Kevin Pan</name>
      <email>bit.kevin@gmail.com</email>
    </author>
    <content>
      <![CDATA[<p>作者：<a href="http://weibo.com/highball">汪海波</a></p><p>其实我们可以这样看待比特币的交易：『交易的发起者悬赏若干比特币，在网络上贴出了一到数学题，谁解出了这道数学题，悬赏就归谁了』。 顺着这个思路，Alice对Bob的转账可以理解为『Alice把一道只有Bob才能解开的数学题发到网络上，Bob解出题并拿走了悬赏』。那么，每个交易数据中都会出现的『脚本』就是题和解，『脚本语言』就是用来描述题和解的工具。</p><p><img src="https://cloud.githubusercontent.com/assets/514951/14377566/e6d18d00-fda2-11e5-9c90-a237ca251bff.png" alt="图01"></p><h2 id="『输入脚本』和『输出脚本』"><a href="#『输入脚本』和『输出脚本』" class="headerlink" title="『输入脚本』和『输出脚本』"></a>『输入脚本』和『输出脚本』</h2><p>在这里我们先讨论单输入单输出的比特币交易，因为这样描述起来更方便且不影响对『脚本』的理解。<br><a href="https://blockchain.info/zh-cn/tx/9c50cee8d50e273100987bb12ec46208cb04a1d5b68c9bea84fd4a04854b5eb1">9c50cee8d50e273100987bb12ec46208cb04a1d5b68c9bea84fd4a04854b5eb1</a> 这是一个单输入单输出交易，看下我们要关注的数据：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">Hash:</span><br><span class="line">9c50cee8d50e273100987bb12ec46208cb04a1d5b68c9bea84fd4a04854b5eb1</span><br><span class="line"></span><br><span class="line">输入交易:</span><br><span class="line">  前导输入的Hash:</span><br><span class="line">  437b95ae15f87c7a8ab4f51db5d3c877b972ef92f26fbc6d3c4663d1bc750149</span><br><span class="line">  </span><br><span class="line">  输入脚本 scriptSig:</span><br><span class="line">  3045022100efe12e2584bbd346bccfe67fd50a54191e4f45f945e3853658284358d9c062ad02200121e00b6297c0874650d00b786971f5b4601e32b3f81afa9f9f8108e93c752201</span><br><span class="line">  038b29d4fbbd12619d45c84c83cb4330337ab1b1a3737250f29cec679d7551148a</span><br><span class="line"></span><br><span class="line">输出交易:</span><br><span class="line">  转账值:</span><br><span class="line">  0.05010000 btc</span><br><span class="line"></span><br><span class="line">  输出脚本 scriptPubKey:</span><br><span class="line">  OP_DUP OP_HASH160 be10f0a78f5ac63e8746f7f2e62a5663eed05788 OP_EQUALVERIFY OP_CHECKSIG</span><br></pre></td></tr></table></figure><p>假设Alice是转账发送者，Bob是接受者。那么『输入交易』表明了Alice要动用的比特币的来源，『输出交易』表明了Alice要转账的数额和转账对象——Bob。那么，你可能要问，数据中的『输入脚本』和『输出脚本』是不是就是题和解？对了一半！</p><p>在<a href="http://www.8btc.com/bitcoin_scripts">Bitcoin Wiki</a>中提到：</p><blockquote><p>原先发送币的一方，控制脚本运行，以便比特币在下一个交易中使用。想花掉币的另一方必须把以前记录的运行为真的脚本，放到输入区。</p></blockquote><p>换句话说，在一个交易中，『输出脚本』是数学题，『输入脚本』是题解，**但不是这道数学题的题解。**我开始看Wiki的时候，在这里遇到了一些障碍，没法理解『输入脚本』和『输出脚本』的联系。但是在考虑交易间的关系后，就明白了。</p><p>假设有这么一系列交易：</p><p><img src="https://cloud.githubusercontent.com/assets/514951/14377658/8eb0c22a-fda3-11e5-8d64-d6a6ab4d361a.png" alt="图02"></p><ol><li>上图的三个交易都是单输入单输出交易</li><li>每个『输入交易』『输出交易』中，都包含对应的『脚本』</li><li><strong>交易a</strong>，Alice转账给Bob；<strong>交易b</strong>，Bob转账给Carol；<strong>交易c</strong>，Carol转账给Dave</li><li>当前交易的『输入』都引用前一个交易的『输出』，如交易b的『输入』引用交易a的『输出』</li></ol><p>按照之前的说法，<strong>交易a</strong>中的『输出脚本』就是Alice为Bob出的数学题。那么，Bob想要引用<strong>交易a</strong>『输出交易』的比特币，就要解开这道数学题。题解是在<strong>交易b</strong>的『输入脚本』里给出的！Bob解开了这道题，获得了奖金，然后在<strong>交易b</strong>中为Carol出一道数学题，等待Carol来解…</p><p>所以说，下图中相同颜色的『输出』和『输入』才是一对题和解：</p><p><img src="https://cloud.githubusercontent.com/assets/514951/14377680/9a8ac6b8-fda3-11e5-965e-80bda1aed8c6.png" alt="图03"></p><h2 id="脚本语言"><a href="#脚本语言" class="headerlink" title="脚本语言"></a>脚本语言</h2><p><a href="http://www.8btc.com/bitcoin_scripts">Bitcoin Wiki</a>给出的对脚本的解释:</p><blockquote><p>比特币在交易中使用脚本系统，与FORTH(一种编译语言)一样，脚本是简单的、基于堆栈的、并且从左向右处理，它特意设计成非图灵完整，没有LOOP语句。</p></blockquote><p>要理解比特币脚本，先要了解『堆栈』，这是一个后进先出(Last In First Out )的容器，脚本系统对数据的操作都是通过它完成的。比特币脚本系统中有两个堆栈：主堆栈和副堆栈，一般来说主要使用主堆栈。举几个简单的例子，看下指令是如何对堆栈操作的（完整的指令集在<a href="http://www.8btc.com/bitcoin_scripts">Wiki</a>里可以找到）:</p><ul><li><p>常数入栈：把一段常数压入到堆栈中，这个常数成为了栈顶元素<br><img src="https://cloud.githubusercontent.com/assets/514951/14377676/9a6d1d16-fda3-11e5-9f83-275c6f95bd61.png" alt="图04"></p></li><li><p>OP_DUP：复制栈顶元素<br><img src="https://cloud.githubusercontent.com/assets/514951/14377672/9a4d5b5c-fda3-11e5-8365-72ff3034ec4d.png" alt="图05"></p></li><li><p>OP_EQUALVERIFY：检查栈顶两个元素是否相等<br><img src="https://cloud.githubusercontent.com/assets/514951/14377674/9a4db156-fda3-11e5-9fbc-74c6119ddb58.png" alt="图06"></p></li></ul><h2 id="标准交易脚本"><a href="#标准交易脚本" class="headerlink" title="标准交易脚本"></a>标准交易脚本</h2><p>也就是P2PKH(Pay To Public Key Hash)，我们常用的转账方式。Alice在转账给Bob的时候，『输出交易』中给出了Bob的『钱包地址』(等价于『公钥哈希』)；当Bob想要转账给Carol的时候，他要证明自己拥有这个『钱包地址』对应的『私钥』，所以在『输入交易』中给出了自己的『公钥』以及使用『私钥』对交易的签名。看个实例：</p><ul><li>交易a: <a href="https://blockchain.info/zh-cn/tx/9c50cee8d50e273100987bb12ec46208cb04a1d5b68c9bea84fd4a04854b5eb1">9c50cee8d50e273100987bb12ec46208cb04a1d5b68c9bea84fd4a04854b5eb1</a></li><li>交易b: <a href="https://blockchain.info/tx/62fadb313b74854a818de4b4c0dc2e2049282b28ec88091a9497321203fb016e">62fadb313b74854a818de4b4c0dc2e2049282b28ec88091a9497321203fb016e</a></li></ul><p>交易b中有一个『输入交易』引用了交易a的『输出交易』，它们的脚本是一对题与解：</p><p>**题：**交易a的『输出脚本』，若干个脚本指令和转账接收方的『公钥哈希』</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">OP_DUP OP_HASH160 be10f0a78f5ac63e8746f7f2e62a5663eed05788 OP_EQUALVERIFY OP_CHECKSIG </span><br></pre></td></tr></table></figure><p>**解：**交易b的『输入脚本』，这么一长串只是两个元素，『签名』和『公钥』（sig &amp; pubkey）</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">3046022100ba1427639c9f67f2ca1088d0140318a98cb1e84f604dc90ae00ed7a5f9c61cab02210094233d018f2f014a5864c9e0795f13735780cafd51b950f503534a6af246aca301</span><br><span class="line">03a63ab88e75116b313c6de384496328df2656156b8ac48c75505cd20a4890f5ab</span><br></pre></td></tr></table></figure><p>下面来看下这两段脚本是如何执行，来完成『解题』过程的。</p><ol><li><p>首先执行的是『输入脚本』。因为脚本是从左向右执行的，那么先入栈的是『签名』，随后是『公钥』<br><img src="https://cloud.githubusercontent.com/assets/514951/14377675/9a4ecf50-fda3-11e5-88b3-4bb3d5bdb538.png" alt="图07"></p></li><li><p>接着，执行的是『输出脚本』。从左向右执行，第一个指令是<code>OP_DUP </code>——复制栈顶元素<br><img src="https://cloud.githubusercontent.com/assets/514951/14377673/9a4d4734-fda3-11e5-8ff3-ab099274fe1e.png" alt="图08"></p></li><li><p><code>OP_HASH160</code>: 计算栈顶元素Hash，得到<code>pubkeyhash </code><br><img src="https://cloud.githubusercontent.com/assets/514951/14377677/9a7c5538-fda3-11e5-82ee-7cbb5789e62f.png" alt="图09"></p></li><li><p>将『输出脚本』中的『公钥哈希』入栈，为了和前面计算得到的哈希区别，称它为<code>pubkeyhash</code><br><img src="https://cloud.githubusercontent.com/assets/514951/14377678/9a7cee4e-fda3-11e5-8745-3cdf582a0b77.png" alt="图10"></p></li><li><p><code>OP_EQUALVERIFY</code>: 检查栈顶前两元素是否相等，如果相等继续执行，否则中断执行，返回失败<br><img src="https://cloud.githubusercontent.com/assets/514951/14377679/9a7ed808-fda3-11e5-887e-d2dd7d9c5143.png" alt="图11"></p></li><li><p><code>OP_CHECKSIG</code>: 使用栈顶前两元素执行签名校验操作，如果相等，返回成功，否则返回失败<br><img src="https://cloud.githubusercontent.com/assets/514951/14377682/9a9f8878-fda3-11e5-9de7-6c7544f12107.png" alt="图12"></p></li></ol><p>这样一串指令执行下来，就可以验证这道数学题是否做对了，也就是说验明了想要花费『钱包地址』中比特币的人是否拥有对应的『私钥』。上面的执行过程是可以在<a href="http://webbtc.com/script">脚本模拟器</a>中执行的，能够看到每一步执行的状态，感兴趣的童鞋可以尝试一下。</p><p>其实除了标准的P2PKH交易脚本，还有P2SH的Multi-Sig脚本以及真正的『解谜交易』脚本，我们可以在今后接着讨论。</p><hr><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p> [1] <a href="http://www.8btc.com/bitcoin_scripts">比特币脚本</a><br> [2] <a href="http://weibo.com/MyBitcoin">申屠青春(我看比特币</a><br> [3] <a href="https://en.bitcoin.it/wiki/Script">Bitcoin Wiki, Script</a></p>]]>
    </content>
    <id>https://kevinpan.page/2016/04/08/understand-bitcoin-script/</id>
    <link href="https://kevinpan.page/2016/04/08/understand-bitcoin-script/"/>
    <published>2016-04-08T17:00:00.000Z</published>
    <summary>
      <![CDATA[<p>作者：<a href="http://weibo.com/highball">汪海波</a></p>
<p>其实我们可以这样看待比特币的交易：『交易的发起者悬赏若干比特币，在网络上贴出了一到数学题，谁解出了这道数学题，悬赏就归谁了』。 顺着这个思路，Alice对Bob的转账]]>
    </summary>
    <title>理解比特币脚本</title>
    <updated>2016-04-08T17:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Kevin Pan</name>
      <email>bit.kevin@gmail.com</email>
    </author>
    <content>
      <![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>加入比特币后达到的效果：只需知道服务提供方的比特币地址，就可以使用其服务。</p><p>假定<code>17J3FrhsiHr6bJTkbV4avLURxtC35b7J1E</code>是服务提供方的比特币地址，那么有如下约定：</p><ul><li><em>17J3FrhsiHr6bJTkbV4avLURxtC35b7J1E</em> 即为服务方的比特币收款地址</li><li><em>17J3FrhsiHr6bJTkbV4avLURxtC35b7J1E</em> 即为共享密钥（配置参数password）</li><li><em>17J3FrhsiHr6bJTkbV4avLURxtC35b7J1E</em><strong>.com</strong> 即为服务方的域名</li></ul><p>假定客户端拥有地址 <code>1CntTWhxxxxxxxxxxxxxxxxxxxxxx</code> 及其私钥。首先保障该地址有一定数量的比特币，然后从该地址支付比特币至服务端地址，当服务方地址收到比特币后，将该客户地址添加到列表文件中，此时，客户即可使用对方的服务。</p><p>项目GitHub: <a href="https://github.com/bitkevin/shadowsocks-libev">https://github.com/bitkevin/shadowsocks-libev</a>，如果shadowsocks接收的话会pull request回原来分支。</p><p>为了学习测试，可以使用测试服务器 <code>17J3FrhsiHr6bJTkbV4avLURxtC35b7J1E.com</code>，费用是每天 0.00015 btc。服务端每分钟更新一次支付记录。</p><h2 id="编译"><a href="#编译" class="headerlink" title="编译"></a>编译</h2><p>编译步骤没有变化，主要是配置文件的差异。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"># build shadowsocks</span><br><span class="line">apt-get install build-essential autoconf libtool libssl-dev</span><br><span class="line">./configure &amp;&amp; make</span><br><span class="line">make install</span><br></pre></td></tr></table></figure><h2 id="服务端"><a href="#服务端" class="headerlink" title="服务端"></a>服务端</h2><p>写入付款地址的记录</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">mkdir -p /var/ss</span><br><span class="line">echo &quot;1CntTWhxxxxxxxxxxxxxxxxxxxxxx&quot; &gt;&gt; /var/ss/list-ss-server.txt</span><br></pre></td></tr></table></figure><p><code>ss-server</code> 启动时，指定参数 <code>--bitcoin-list</code> 即可：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ss-server -s 0.0.0.0 -p 443 -k 17J3FrhsiHr6bJTkbV4avLURxtC35b7J1E -m aes-256-cfb -t 60 --workers 10 --fast-open --bitcoin-list /var/ss/list-ss-server.txt</span><br></pre></td></tr></table></figure><p>同时，注册域名 <code>17J3FrhsiHr6bJTkbV4avLURxtC35b7J1E.com</code>，并将其A记录指向到提供服务的IP地址上。</p><h2 id="客户端"><a href="#客户端" class="headerlink" title="客户端"></a>客户端</h2><p>示例配置文件：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    &quot;server&quot;:&quot;17J3FrhsiHr6bJTkbV4avLURxtC35b7J1E.com&quot;,</span><br><span class="line">    &quot;server_port&quot;:443,</span><br><span class="line">    &quot;local_port&quot;:8999,</span><br><span class="line">    &quot;password&quot;:&quot;17J3FrhsiHr6bJTkbV4avLURxtC35b7J1E&quot;,</span><br><span class="line">    &quot;timeout&quot;:300,</span><br><span class="line">    &quot;method&quot;:&quot;aes-256-cfb&quot;,</span><br><span class="line">    &quot;bitcoin_address&quot;:&quot;1CntTWhxxxxxxxxxxxxxxxxxxxxxx&quot;,</span><br><span class="line">    &quot;bitcoin_privkey&quot;:&quot;L5eixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&quot;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><code>bitcoin_address</code> 支付地址，推荐新生成一个地址单独用于此，并保证该地址是你个人控制的</li><li><code>bitcoin_privkey</code> 支付地址对应的私钥，base58格式，大部分钱包导出就是此格式。该私钥不会暴露至网络</li></ul><p>保障地址 <code>1CntTWhxxxxxxxxxxxxxxxxxxxxxx</code> 有一定数量的比特币，然后从该地址付款至服务端地址 <code>17J3FrhsiHr6bJTkbV4avLURxtC35b7J1E</code>，付款比特币数量尽量以天为整数即可，例如付款30天的费用：0.0045 btc &#x3D; 0.00015 * 30。</p><h2 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h2><p>为了方便，提供了一个自动生成付款地址记录的脚本。安装至目录 <code>/var/ss</code>：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">apt-get install php5-cli php5-curl</span><br><span class="line">mkdir -p /var/ss</span><br><span class="line">cp scripts/generate.php /var/ss</span><br></pre></td></tr></table></figure><p>拷贝后，需要修改其配置，generate.php 中的配置参数说明：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">// 由于使用了 chain.com 的API，所以到 chain.com 注册一个账户，并免费获得一个API KEY。</span><br><span class="line">$_CFG[&#x27;API-KEY-ID&#x27;] = &quot;DEMO-4a5e1e4&quot;;</span><br><span class="line"></span><br><span class="line">// 服务端的地址，该地址用于收款等。</span><br><span class="line">$_CFG[&#x27;server_baddress&#x27;] = &quot;17J3FrhsiHr6bJTkbV4avLURxtC35b7J1E&quot;;</span><br><span class="line"></span><br><span class="line">// 每天的价格，单位是聪。 15000 Satoshi = 0.00015 Btc。</span><br><span class="line">$_CFG[&#x27;satoshi_per_day&#x27;] = 15000;</span><br></pre></td></tr></table></figure><p>若 <code>ss-local</code>&#x2F;<code>ss-server</code> 的配置文件中去掉 <code>--bitcoin-xxx</code> 参数，则与不带bitcoin支持的shadowsocks行为一致。</p>]]>
    </content>
    <id>https://kevinpan.page/2015/03/11/shadowsocks-add-bitcoin-support/</id>
    <link href="https://kevinpan.page/2015/03/11/shadowsocks-add-bitcoin-support/"/>
    <published>2015-03-11T21:30:19.000Z</published>
    <summary>
      <![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>加入比特币后达到的效果：只需知道服务提供方的比特币地址，就可以使用其服务。</p>
<p>假定<code>17J3FrhsiHr6bJTkb]]>
    </summary>
    <title>Shadowsocks 加入比特币的支持</title>
    <updated>2015-03-11T21:30:19.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Kevin Pan</name>
      <email>bit.kevin@gmail.com</email>
    </author>
    <content>
      <![CDATA[<p>从财富不可侵犯角度讲，比特币是人类史上最安全的货币。但令人郁闷的是，安全地存储比特币却遇到诸多麻烦。私钥机制使得其神圣不可侵犯，也使其资金安全遇到麻烦。</p><p>个人存储管理比特币非常不现实，安全性差，丢币概率高。在线钱包显得更加安全可靠一些，主要分两类：</p><ul><li>链下(Off-Chain)，链下钱包服务即银行模式，若服务商出现任何问题，储户资金即出问题</li><li>链上(On-Chain)，链上钱包“理论上”安全，因其不掌握用户私钥（仅保存私钥的密文），但也不绝对，例如服务器被入侵后，钱包关键代码被恶意修改，导致能够恢复出用户私钥，无论多少币，均瞬间被转移</li></ul><p>没有绝对安全的方案，只有相对安全实现，就是把出问题的概率降低得非常非常低。程序发生严重Bug、服务器被入侵这些都不能认为是极低概率事件，只是小概率而已，时间足够长的话几乎都会发生，回溯比特币这几年历史，发生过很多小概率、黑天鹅事件。</p><p>这里提出一种钱包的实现方案，尝试去解决一些安全隐患问题。方案分为服务端，客户端两个部分。</p><h4 id="服务端"><a href="#服务端" class="headerlink" title="服务端"></a>服务端</h4><ul><li>数据服务。只读模式，包括不限于：<ul><li>balance by address</li><li>unspent outputs</li><li>tx&#x2F;block query</li></ul></li><li>广播服务。输入是公开的数据，协助将交易(Raw Tx)广播至比特币网络</li></ul><h4 id="客户端"><a href="#客户端" class="headerlink" title="客户端"></a>客户端</h4><ul><li>硬件部分<ul><li>支持如ECDSA, SHA256、Ripemd160等</li><li>支持硬件隔离技术，存储敏感数据，如私钥数据</li></ul></li><li>APP部分<ul><li>与硬件部分进行通信</li><li>提供功能界面，实现钱包操作交互</li></ul></li></ul><p>客户端硬件部分像拉卡拉类似的一个东西，可以通过耳机等音频设备与手机APP通信。</p><ul><li>收款：当用户使用钱包时，插入硬件部分，启动APP，从硬件读取比特币地址列表，并去服务端查询地址数据，更新余额</li><li>发款：APP根据比特币地址从服务端获取相关未花费交易，用户设置转出地址和金额，APP根据未花费交易构建待签名的空交易，将空交易传输至硬件并通知其签名。硬件从隔离区读取数据并由加密芯片完成签名后，将完整交易放入普通数据区，APP从普通数据区读取签名后的交易并提交至服务端广播</li><li>数据备份：硬件支持AES双向加密算法，用户在APP输入备份密码，传至硬件，硬件从隔离区读取数据并由加密芯片完成AES加密，将加密后的数据放入普通数据区，由APP传输至手机、云盘等完成备份</li><li>硬件遗失：为了防止遗失导致硬件破解丢币，敏感数据在隔离区中平时即以加密形式存在，每次使用时，输入进入密码，才能使用</li></ul><p>安全点：</p><ul><li>服务端必须是只读模式的。只读的设计，保障客户端不会提交敏感数据，即使服务端被黑，也无法截获用户私钥</li><li>客户端必须实现硬件隔离，保护私钥等敏感数据，使得操作系统无法读取敏感信息，木马、恶意软件就没有办法盗取</li></ul><p>这只是一个初步的想法，可能存在重大缺陷，也许有团队已经在实现了，贴出来希望大家一起讨论。</p>]]>
    </content>
    <id>https://kevinpan.page/2014/06/15/safe-wallet-idea/</id>
    <link href="https://kevinpan.page/2014/06/15/safe-wallet-idea/"/>
    <published>2014-06-16T00:40:21.000Z</published>
    <summary>
      <![CDATA[<p>从财富不可侵犯角度讲，比特币是人类史上最安全的货币。但令人郁闷的是，安全地存储比特币却遇到诸多麻烦。私钥机制使得其神圣不可侵犯，也使其资金安全遇到麻烦。</p>
<p>个人存储管理比特币非常不现实，安全性差，丢币概率高。在线钱包显得更加安全可靠一些，主要分两类：</p>
<]]>
    </summary>
    <title>比特币安全钱包的想法</title>
    <updated>2014-06-16T00:40:21.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Kevin Pan</name>
      <email>bit.kevin@gmail.com</email>
    </author>
    <content>
      <![CDATA[<p>本主题已转移至新地址： <a href="http://blog.open-nodes.org/2014/06/14/open-nodes-plan/">http://blog.open-nodes.org/2014/06/14/open-nodes-plan/</a></p>]]>
    </content>
    <id>https://kevinpan.page/2014/06/14/open-nodes-plan/</id>
    <link href="https://kevinpan.page/2014/06/14/open-nodes-plan/"/>
    <published>2014-06-14T16:37:31.000Z</published>
    <summary>
      <![CDATA[<p>本主题已转移至新地址： <a href="http://blog.open-nodes.org/2014/06/14/open-nodes-plan/">http://blog.open-nodes.org/2014/06/14/open-nodes-plan/</a></]]>
    </summary>
    <title>开放节点计划</title>
    <updated>2014-06-14T16:37:31.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Kevin Pan</name>
      <email>bit.kevin@gmail.com</email>
    </author>
    <content>
      <![CDATA[<h2 id="交易的构造、签名与广播"><a href="#交易的构造、签名与广播" class="headerlink" title="交易的构造、签名与广播"></a>交易的构造、签名与广播</h2><p>上篇介绍了交易结构、签名等，为了更直观的认识比特币，借助<strong>bitcoind</strong>演示手动构造并广播交易的完整过程。</p><h3 id="普通交易"><a href="#普通交易" class="headerlink" title="普通交易"></a>普通交易</h3><h4 id="1-找出未花费的币（unspent-output）"><a href="#1-找出未花费的币（unspent-output）" class="headerlink" title="1. 找出未花费的币（unspent output）"></a>1. 找出未花费的币（unspent output）</h4><p>通过命令：<code>listunspent [minconf=1] [maxconf=9999999]  [&quot;address&quot;,...]</code>列出某个地址未花费的币(交易)，<code>minconf</code>&#x2F;<code>maxconf</code>表示该笔收入交易的确认数范围，如果需要列出还未确认的交易，需将<code>minconf</code>设置为0。</p><p>执行：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">bitcoind listunspent 0 100 &#x27;[&quot;1Lab618UuWjLmVA1Q64tHZXcLoc4397ZX3&quot;]&#x27;</span><br></pre></td></tr></table></figure><p>输出：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">[</span><br><span class="line">    &#123;</span><br><span class="line">        &quot;txid&quot; : &quot;296ea7bf981b44999d689853d17fe0ceb852a8a34e68fcd19f0a41e589132156&quot;,</span><br><span class="line">        &quot;vout&quot; : 0,</span><br><span class="line">        &quot;address&quot; : &quot;1Lab618UuWjLmVA1Q64tHZXcLoc4397ZX3&quot;,</span><br><span class="line">        &quot;account&quot; : &quot;&quot;,</span><br><span class="line">        &quot;scriptPubKey&quot; : &quot;76a914d6c492056f3f99692b56967a42b8ad44ce76b67a88ac&quot;,</span><br><span class="line">        &quot;amount&quot; : 0.19900000,</span><br><span class="line">        &quot;confirmations&quot; : 1</span><br><span class="line">    &#125;</span><br><span class="line">]</span><br></pre></td></tr></table></figure><p>我们找到该地址的一个未花费交易，位于交易<a href="http://blockchain.info/tx/296ea7bf981b44999d689853d17fe0ceb852a8a34e68fcd19f0a41e589132156">296ea7bf981b4499…9f0a41e589132156</a>的第0个位置。</p><h4 id="2-创建待发送交易"><a href="#2-创建待发送交易" class="headerlink" title="2. 创建待发送交易"></a>2. 创建待发送交易</h4><p>创建待发送交易，由命令：<code>createrawtransaction [{&quot;txid&quot;:txid,&quot;vout&quot;:n},...] {address:amount,...}</code>来完成。我们将 <em>0.1</em> BTC发送至 <em>1Q8s4qDRbCbFypG5AFNR9tFC57PStkPX1x</em> ，并支付 <em>0.0001</em> BTC做为矿工费。输入交易的额度为 <em>0.199</em> ，输出为 <em>0.1 + 0.0001 &#x3D; 0.1001</em> ，那么还剩余： <em>0.199 - 0.1001 &#x3D; 0.0989</em> ，将此作为找零发回给自己。</p><p>执行：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">bitcoind createrawtransaction \</span><br><span class="line">&#x27;[&#123;&quot;txid&quot;:&quot;296ea7bf981b44999d689853d17fe0ceb852a8a34e68fcd19f0a41e589132156&quot;,&quot;vout&quot;:0&#125;]&#x27; \</span><br><span class="line">&#x27;&#123;&quot;1Q8s4qDRbCbFypG5AFNR9tFC57PStkPX1x&quot;:0.1, &quot;1Lab618UuWjLmVA1Q64tHZXcLoc4397ZX3&quot;:0.0989&#125;&#x27;</span><br></pre></td></tr></table></figure><p>输出：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">010000000156211389e5410a9fd1fc684ea3a852b8cee07fd15398689d99441b98bfa76e290000000000ffffffff0280969800000000001976a914fdc7990956642433ea75cabdcc0a9447c5d2b4ee88acd0e89600000000001976a914d6c492056f3f99692b56967a42b8ad44ce76b67a88ac00000000</span><br></pre></td></tr></table></figure><p>通过命令：<code>decoderawtransaction &lt;hex string&gt;</code>，可以将此段十六进制字符串解码。</p><p>执行：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">bitcoind decoderawtransaction &#x27;010000000156211389e5410a9fd1fc684ea3a852b8cee07fd15398689d99441b98bfa76e290000000000ffffffff0280969800000000001976a914fdc7990956642433ea75cabdcc0a9447c5d2b4ee88acd0e89600000000001976a914d6c492056f3f99692b56967a42b8ad44ce76b67a88ac00000000&#x27;</span><br></pre></td></tr></table></figure><p>输出：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    &quot;txid&quot; : &quot;54f773a3fdf7cb3292fc76b46c97e536348b3a0715886dbfd2f60e115fb3a8f0&quot;,</span><br><span class="line">    &quot;version&quot; : 1,</span><br><span class="line">    &quot;locktime&quot; : 0,</span><br><span class="line">    &quot;vin&quot; : [</span><br><span class="line">        &#123;</span><br><span class="line">            &quot;txid&quot; : &quot;296ea7bf981b44999d689853d17fe0ceb852a8a34e68fcd19f0a41e589132156&quot;,</span><br><span class="line">            &quot;vout&quot; : 0,</span><br><span class="line">            &quot;scriptSig&quot; : &#123;</span><br><span class="line">                &quot;asm&quot; : &quot;&quot;,</span><br><span class="line">                &quot;hex&quot; : &quot;&quot;</span><br><span class="line">            &#125;,</span><br><span class="line">            &quot;sequence&quot; : 4294967295</span><br><span class="line">        &#125;</span><br><span class="line">    ],</span><br><span class="line">    &quot;vout&quot; : [</span><br><span class="line">        &#123;</span><br><span class="line">            &quot;value&quot; : 0.10000000,</span><br><span class="line">            &quot;n&quot; : 0,</span><br><span class="line">            &quot;scriptPubKey&quot; : &#123;</span><br><span class="line">                &quot;asm&quot; : &quot;OP_DUP OP_HASH160 fdc7990956642433ea75cabdcc0a9447c5d2b4ee OP_EQUALVERIFY OP_CHECKSIG&quot;,</span><br><span class="line">                &quot;hex&quot; : &quot;76a914fdc7990956642433ea75cabdcc0a9447c5d2b4ee88ac&quot;,</span><br><span class="line">                &quot;reqSigs&quot; : 1,</span><br><span class="line">                &quot;type&quot; : &quot;pubkeyhash&quot;,</span><br><span class="line">                &quot;addresses&quot; : [</span><br><span class="line">                    &quot;1Q8s4qDRbCbFypG5AFNR9tFC57PStkPX1x&quot;</span><br><span class="line">                ]</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;,</span><br><span class="line">        &#123;</span><br><span class="line">            &quot;value&quot; : 0.09890000,</span><br><span class="line">            &quot;n&quot; : 1,</span><br><span class="line">            &quot;scriptPubKey&quot; : &#123;</span><br><span class="line">                &quot;asm&quot; : &quot;OP_DUP OP_HASH160 d6c492056f3f99692b56967a42b8ad44ce76b67a OP_EQUALVERIFY OP_CHECKSIG&quot;,</span><br><span class="line">                &quot;hex&quot; : &quot;76a914d6c492056f3f99692b56967a42b8ad44ce76b67a88ac&quot;,</span><br><span class="line">                &quot;reqSigs&quot; : 1,</span><br><span class="line">                &quot;type&quot; : &quot;pubkeyhash&quot;,</span><br><span class="line">                &quot;addresses&quot; : [</span><br><span class="line">                    &quot;1Lab618UuWjLmVA1Q64tHZXcLoc4397ZX3&quot;</span><br><span class="line">                ]</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    ]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>至此，一个“空白交易”就构造好了，尚未使用私钥对交易进行签名，字段<code>scriptSig</code>是留空的，无签名的交易是无效的。此时的Tx ID并不是最终的Tx ID，填入签名后Tx ID会发生变化。</p><p>在手动创建交易时，务必注意输入、输出的值，<code>非常容易犯错的是忘记构造找零输出</code>（如非必要勿手动构造交易）。曾经有人构造交易时忘记找零，发生了<a href="https://blockchain.info/tx/4ed20e0768124bc67dc684d57941be1482ccdaa45dadb64be12afba8c8554537">支付 <strong>200 BTC</strong> 的矿工费</a>的人间惨剧，所幸的是收录该笔交易的Block由著名挖矿团队“烤猫（Friedcat）”挖得，该团队非常厚道的<a href="https://blockchain.info/tx/b18abce37b48a5f434f108ae7ce34f22aa2bfbd9eb9310314029e4b9e3c7cf95">退回了多余费用</a>。</p><h4 id="3-签名"><a href="#3-签名" class="headerlink" title="3. 签名"></a>3. 签名</h4><p>交易签名使用命令：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">signrawtransaction &lt;hex string&gt; \</span><br><span class="line">[&#123;&quot;txid&quot;:txid,&quot;vout&quot;:n,&quot;scriptPubKey&quot;:hex,&quot;redeemScript&quot;:hex&#125;,...] [&lt;privatekey1&gt;,...] \</span><br><span class="line">[sighashtype=&quot;ALL&quot;]</span><br></pre></td></tr></table></figure><ul><li>第一个参数是创建的待签名交易的十六进制字符串；</li><li>第二个参数有点类似创建交易时的参数，不过需要多出一个公钥字段<code>scriptPubKey</code>，其他节点验证交易时是通过公钥和签名来完成的，所以要提供公钥；如果是合成地址，则需要提供<code>redeemScript</code>；</li><li>第三个参数是即将花费的币所在地址的私钥，用来对交易进行签名，如果该地址私钥已经导入至bitcoind中，则无需显式提供；</li><li>最后一个参数表示签名类型，在上一篇里，介绍了三种交易签名类型；</li></ul><p>签名之前需要找到<code>scriptPubKey</code>，提取输入交易信息即可获取(也可以根据其公钥自行计算)，由命令：<code>getrawtransaction &lt;txid&gt; [verbose=0]</code>完成。</p><p>执行：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">bitcoind getrawtransaction 296ea7bf981b44999d689853d17fe0ceb852a8a34e68fcd19f0a41e589132156 1</span><br></pre></td></tr></table></figure><p>输出：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    &quot;hex&quot; : &quot;01000000010511331f639e974283d3909496787a660583dc88f41598d177e225b5f352314a000000006c493046022100be8c796122ec598295e6dfd6664a20a7e20704a17f76d3d925c9ec421ca60bc1022100cf9f2d7b9f24285f7c119c91f24521e5483f6b141de6ee55658fa70116ee04d4012103cad07f6de0b181891b5291a5bc82b228fe6509699648b0b53556dc0057eeb5a4ffffffff0160a62f01000000001976a914d6c492056f3f99692b56967a42b8ad44ce76b67a88ac00000000&quot;,</span><br><span class="line">    &quot;txid&quot; : &quot;296ea7bf981b44999d689853d17fe0ceb852a8a34e68fcd19f0a41e589132156&quot;,</span><br><span class="line">    &quot;version&quot; : 1,</span><br><span class="line">    &quot;locktime&quot; : 0,</span><br><span class="line">    &quot;vin&quot; : [</span><br><span class="line">        &#123;</span><br><span class="line">            &quot;txid&quot; : &quot;4a3152f3b525e277d19815f488dc8305667a78969490d38342979e631f331105&quot;,</span><br><span class="line">            &quot;vout&quot; : 0,</span><br><span class="line">            &quot;scriptSig&quot; : &#123;</span><br><span class="line">                &quot;asm&quot; : &quot;3046022100be8c796122ec598295e6dfd6664a20a7e20704a17f76d3d925c9ec421ca60bc1022100cf9f2d7b9f24285f7c119c91f24521e5483f6b141de6ee55658fa70116ee04d401 03cad07f6de0b181891b5291a5bc82b228fe6509699648b0b53556dc0057eeb5a4&quot;,</span><br><span class="line">                &quot;hex&quot; : &quot;493046022100be8c796122ec598295e6dfd6664a20a7e20704a17f76d3d925c9ec421ca60bc1022100cf9f2d7b9f24285f7c119c91f24521e5483f6b141de6ee55658fa70116ee04d4012103cad07f6de0b181891b5291a5bc82b228fe6509699648b0b53556dc0057eeb5a4&quot;</span><br><span class="line">            &#125;,</span><br><span class="line">            &quot;sequence&quot; : 4294967295</span><br><span class="line">        &#125;</span><br><span class="line">    ],</span><br><span class="line">    &quot;vout&quot; : [</span><br><span class="line">        &#123;</span><br><span class="line">            &quot;value&quot; : 0.19900000,</span><br><span class="line">            &quot;n&quot; : 0,</span><br><span class="line">            &quot;scriptPubKey&quot; : &#123;</span><br><span class="line">                &quot;asm&quot; : &quot;OP_DUP OP_HASH160 d6c492056f3f99692b56967a42b8ad44ce76b67a OP_EQUALVERIFY OP_CHECKSIG&quot;,</span><br><span class="line">                &quot;hex&quot; : &quot;76a914d6c492056f3f99692b56967a42b8ad44ce76b67a88ac&quot;,</span><br><span class="line">                &quot;reqSigs&quot; : 1,</span><br><span class="line">                &quot;type&quot; : &quot;pubkeyhash&quot;,</span><br><span class="line">                &quot;addresses&quot; : [</span><br><span class="line">                    &quot;1Lab618UuWjLmVA1Q64tHZXcLoc4397ZX3&quot;</span><br><span class="line">                ]</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    ],</span><br><span class="line">    &quot;blockhash&quot; : &quot;000000000000000488f18f7659acd85b2bd06a5ed2c4439eea74a8b968d16656&quot;,</span><br><span class="line">    &quot;confirmations&quot; : 19,</span><br><span class="line">    &quot;time&quot; : 1383235737,</span><br><span class="line">    &quot;blocktime&quot; : 1383235737</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>scriptPubKey</code>位于”vout”[0]-&gt;”scriptPubKey”-&gt;”hex”，即： <em>76a914d6c492056f3f99692b56967a42b8ad44ce76b67a88ac</em> 。</p><p>签名使用ECDSA算法，对其，“空白交易”签名之，执行：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">bitcoind signrawtransaction \</span><br><span class="line">&quot;010000000156211389e5410a9fd1fc684ea3a852b8cee07fd15398689d99441b98bfa76e290000000000ffffffff0280969800000000001976a914fdc7990956642433ea75cabdcc0a9447c5d2b4ee88acd0e89600000000001976a914d6c492056f3f99692b56967a42b8ad44ce76b67a88ac00000000&quot; \</span><br><span class="line">&#x27;[&#123;&quot;txid&quot;:&quot;296ea7bf981b44999d689853d17fe0ceb852a8a34e68fcd19f0a41e589132156&quot;,&quot;vout&quot;:0,&quot;scriptPubKey&quot;:&quot;76a914d6c492056f3f99692b56967a42b8ad44ce76b67a88ac&quot;&#125;]&#x27;</span><br></pre></td></tr></table></figure><p>输出：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    &quot;hex&quot; : &quot;010000000156211389e5410a9fd1fc684ea3a852b8cee07fd15398689d99441b98bfa76e29000000008c493046022100f9da4f53a6a4a8317f6e7e9cd9a7b76e0f5e95dcdf70f1b1e2b3548eaa3a6975022100858d48aed79da8873e09b0e41691f7f3e518ce9a88ea3d03f7b32eb818f6068801410477c075474b6798c6e2254d3d06c1ae3b91318ca5cc62d18398697208549f798e28efb6c55971a1de68cca81215dd53686c31ad8155cdc03563bf3f73ce87b4aaffffffff0280969800000000001976a914fdc7990956642433ea75cabdcc0a9447c5d2b4ee88acd0e89600000000001976a914d6c492056f3f99692b56967a42b8ad44ce76b67a88ac00000000&quot;,</span><br><span class="line">    &quot;complete&quot; : true</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>签名后，签名值会填入上文所述的空字段中，从而得到一个完整的交易。可通过上文介绍的命令<code>decoderawtransaction &lt;hex string&gt;</code>解码查看之。</p><p>最后一步，就是将其广播出去，等待网络传播至所有节点，约10~60秒广播至全球节点，取决与你的节点的网络连接状况。稍后一些时刻，就会进入Block中。广播由命令<code>sendrawtransaction &lt;hex string&gt;</code>来完成。如果没有运行节点，可以通过公共节点的API进行广播，例如：<a href="https://blockchain.info/pushtx">blockchain.info&#x2F;pushtx</a>。</p><p>执行：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">bitcoind sendrawtransaction \</span><br><span class="line">&quot;010000000156211389e5410a9fd1fc684ea3a852b8cee07fd15398689d99441b98bfa76e29000000008c493046022100f9da4f53a6a4a8317f6e7e9cd9a7b76e0f5e95dcdf70f1b1e2b3548eaa3a6975022100858d48aed79da8873e09b0e41691f7f3e518ce9a88ea3d03f7b32eb818f6068801410477c075474b6798c6e2254d3d06c1ae3b91318ca5cc62d18398697208549f798e28efb6c55971a1de68cca81215dd53686c31ad8155cdc03563bf3f73ce87b4aaffffffff0280969800000000001976a914fdc7990956642433ea75cabdcc0a9447c5d2b4ee88acd0e89600000000001976a914d6c492056f3f99692b56967a42b8ad44ce76b67a88ac00000000&quot;</span><br></pre></td></tr></table></figure><p>输出：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">b5f8da1ea9e02ec3cc0765f9600f49945e94ed4b0c88ed0648896bf3e213205d</span><br></pre></td></tr></table></figure><p>返回的是Transaction Hash值，即<a href="https://blockchain.info/tx/b5f8da1ea9e02ec3cc0765f9600f49945e94ed4b0c88ed0648896bf3e213205d">该交易</a>的ID。至此，交易构造、签名、发送的完整过程完成了。</p><h3 id="合成地址交易"><a href="#合成地址交易" class="headerlink" title="合成地址交易"></a>合成地址交易</h3><p>合成地址以3开头，可以实现多方管理资产，极大提高安全性，也可以轻松实现基于比特币原生的三方交易担保支付。一个<code>M-of-N</code>的模式：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">m &#123;pubkey&#125;...&#123;pubkey&#125; n OP_CHECKMULTISIG</span><br></pre></td></tr></table></figure><p>M和N需满足：</p><ul><li><code>1&lt;=N&lt;=3</code></li><li><code>1&lt;=M&lt;=N</code></li></ul><p>可以是<code>1 of 1</code>，<code>1 of 2</code>，<code>2 of 3</code>等组合，通常选择<code>N=3</code>：</p><ul><li><code>1 of 3</code>，最大程度私钥冗余。防丢私钥损失，3把私钥中任意一把即可签名发币，即使丢失2把都可以保障不受损失；</li><li><code>2 of 3</code>，提高私钥冗余度的同时解决单点信任问题。3把私钥任意2把私钥可签名发币，三方不完全信任的情形，即中介交易中，非常适用；</li><li><code>3 of 3</code>，最大程度解决资金信任问题，无私钥冗余。必须3把私钥全部签名才能发币，适用多方共同管理重要资产，但任何一方遗失私钥均造成严重损失；</li></ul><p>合成地址的交易构造、签名、发送过程与普通交易类似，这里只介绍如何创建一个合成地址。大神Gavin Andresen已经演示过，下面内容摘自其<a href="https://gist.github.com/gavinandresen/3966071">gist</a>.</p><p>首先，需要三对公钥、私钥。公钥创建地址、私钥用于签名。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"># No.1</span><br><span class="line">0491bba2510912a5bd37da1fb5b1673010e43d2c6d812c514e91bfa9f2eb129e1c183329db55bd868e209aac2fbc02cb33d98fe74bf23f0c235d6126b1d8334f86 / 5JaTXbAUmfPYZFRwrYaALK48fN6sFJp4rHqq2QSXs8ucfpE4yQU</span><br><span class="line"># No.2 </span><br><span class="line">04865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253e69256dac09ef122b1a986818a7cb624532f062c1d1f8722084861c5c3291ccffef4ec6874 / 5Jb7fCeh1Wtm4yBBg3q3XbT6B525i17kVhy3vMC9AqfR6FH2qGk</span><br><span class="line"># No.3</span><br><span class="line">048d2455d2403e08708fc1f556002f1b6cd83f992d085097f9974ab08a28838f07896fbab08f39495e15fa6fad6edbfb1e754e35fa1c7844c41f322a1863d46213 / 5JFjmGo5Fww9p8gvx48qBYDJNAzR9pmH5S389axMtDyPT8ddqmw</span><br></pre></td></tr></table></figure><p>使用命令：<code>createmultisig &lt;nrequired&gt; &lt;&#39;[&quot;key&quot;,&quot;key&quot;]&#39;&gt;</code>来合成，其中<code>key</code>为公钥，创建地址时仅需公钥。创建类型是<code>2 of 3</code>.</p><p>输入：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">bitcoind createmultisig 2 \</span><br><span class="line">&#x27;[&quot;0491bba2510912a5bd37da1fb5b1673010e43d2c6d812c514e91bfa9f2eb129e1c183329db55bd868e209aac2fbc02cb33d98fe74bf23f0c235d6126b1d8334f86&quot;,&quot;04865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253e69256dac09ef122b1a986818a7cb624532f062c1d1f8722084861c5c3291ccffef4ec6874&quot;,&quot;048d2455d2403e08708fc1f556002f1b6cd83f992d085097f9974ab08a28838f07896fbab08f39495e15fa6fad6edbfb1e754e35fa1c7844c41f322a1863d46213&quot;]&#x27;</span><br></pre></td></tr></table></figure><p>输出：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    &quot;address&quot; : &quot;3QJmV3qfvL9SuYo34YihAf3sRCW3qSinyC&quot;,</span><br><span class="line">    &quot;redeemScript&quot; : &quot;52410491bba2510912a5bd37da1fb5b1673010e43d2c6d812c514e91bfa9f2eb129e1c183329db55bd868e209aac2fbc02cb33d98fe74bf23f0c235d6126b1d8334f864104865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253e69256dac09ef122b1a986818a7cb624532f062c1d1f8722084861c5c3291ccffef4ec687441048d2455d2403e08708fc1f556002f1b6cd83f992d085097f9974ab08a28838f07896fbab08f39495e15fa6fad6edbfb1e754e35fa1c7844c41f322a1863d4621353ae&quot;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>得到的合成地址是：<code>3QJmV3qfvL9SuYo34YihAf3sRCW3qSinyC</code>，该地址没有公钥，仅有<code>redeemScript</code>，作用与公钥相同。后续的构造、签名、发送过程与上文普通地址交易类似，略去。</p>]]>
    </content>
    <id>https://kevinpan.page/2013/10/27/bitcoin-transaction-02/</id>
    <link href="https://kevinpan.page/2013/10/27/bitcoin-transaction-02/"/>
    <published>2013-10-28T00:54:00.000Z</published>
    <summary>
      <![CDATA[<h2 id="交易的构造、签名与广播"><a href="#交易的构造、签名与广播" class="headerlink" title="交易的构造、签名与广播"></a>交易的构造、签名与广播</h2><p>上篇介绍了交易结构、签名等，为了更直观的认识比特币，借助<stron]]>
    </summary>
    <title>比特币交易构成（二）</title>
    <updated>2013-10-28T00:54:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Kevin Pan</name>
      <email>bit.kevin@gmail.com</email>
    </author>
    <content>
      <![CDATA[<h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><p>交易(Transaction)是比特币系统的信息载体，最小单元。而块(Block)就是将这些基础单元打包装箱，贴上封条，并串联起来。巨大算力保障了块的安全，也就保障了单个交易的安全。</p><h2 id="类型"><a href="#类型" class="headerlink" title="类型"></a>类型</h2><p>交易有三种常见类型：产量交易(Generation)，合成地址交易(Script Hash)，通用地址交易(Pubkey Hash)。该分类并非严格意义的，只是根据交易的输入输出做的简单区分。</p><h3 id="Generation-TX"><a href="#Generation-TX" class="headerlink" title="Generation TX"></a>Generation TX</h3><p>每个Block都对应一个产量交易(Generation TX)，该类交易是没有输入交易的，挖出的新币是所有币的源头。</p><h3 id="Script-Hash-TX"><a href="#Script-Hash-TX" class="headerlink" title="Script Hash TX"></a>Script Hash TX</h3><p>该类交易目前不是很常见，大部分人可能没有听说过，但是非常有意义。未来应该会在某些场合频繁使用。该类交易的接受地址不是通常意义的地址，而是一个合成地址，以3开头（对，以3开头的也是比特币地址！）。三对公私钥，可以生成一个合成地址。在生成过程时指定<code>n of 3</code>中的n，n范围是<code>[1, 3]</code>，若n&#x3D;1，则仅需一个私钥签名即可花费该地址的币，若n&#x3D;3，则需要三把私钥依次签名才可以。</p><h3 id="Pubkey-Hash-TX"><a href="#Pubkey-Hash-TX" class="headerlink" title="Pubkey Hash TX"></a>Pubkey Hash TX</h3><p>该类是最常见的交易类型，由N个输入、M个输出构成。</p><h2 id="数据结构"><a href="#数据结构" class="headerlink" title="数据结构"></a>数据结构</h2><p>交易中存放的是货币所有权的流转信息，所有权登记在比特币地址上(Public Key)。这些信息是全网公开的，以明文形式存储（比特币系统里的所有数据都是明文的），只有当需要转移货币所有权时，才需要用私钥签名来验证。</p><table><thead><tr><th>字段大小</th><th>描述</th><th>数据类型</th><th>解释</th></tr></thead><tbody><tr><td>4</td><td>version, 版本</td><td>uint32_t</td><td>交易数据结构的版本号</td></tr><tr><td>1+</td><td>tx_in count, 输入数量</td><td>var_int</td><td>输入交易的数量</td></tr><tr><td>41+</td><td>tx_in</td><td>tx_in[]</td><td>输入交易的数组，每个输入&gt;&#x3D;41字节</td></tr><tr><td>1+</td><td>tx_out count, 输出数量</td><td>var_int</td><td>输出地址的数量</td></tr><tr><td>9+</td><td>tx_out</td><td>tx_out[]</td><td>输入地址的数组，每个输入&gt;&#x3D;9字节</td></tr><tr><td>4</td><td>lock_time, 锁定时间</td><td>uint32_t</td><td>见下方解释</td></tr></tbody></table><p><code>lock_time</code>是一个多意字段，表示在某个高度的Block之前或某个时间点之前该交易处于锁定态，无法收录进Block。</p><table><thead><tr><th>值</th><th>含义</th></tr></thead><tbody><tr><td>0</td><td>立即生效</td></tr><tr><td>&lt; 500000000</td><td>含义为Block高度，处于该Block之前为锁定（不生效）</td></tr><tr><td>&gt;&#x3D; 500000000</td><td>含义为Unix时间戳，处于该时刻之前为锁定（不生效）</td></tr></tbody></table><p>若该笔交易的所有输入交易的<code>sequence</code>字段，均为INT32最大值(0xffffffff)，则忽略<code>lock_time</code>字段。否则，该交易在未达到Block高度或达到某个时刻之前，是不会被收录进Block中的。</p><h3 id="示例"><a href="#示例" class="headerlink" title="示例"></a>示例</h3><p>为了演示方便，我们读取稍早期的块数据，以高度116219 Block为例。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"># ~  bitcoind getblock 0000000000007c639f2cbb23e4606a1d022fa4206353b9d92e99f5144bd74611           </span><br><span class="line">&#123;</span><br><span class="line">    &quot;hash&quot; : &quot;0000000000007c639f2cbb23e4606a1d022fa4206353b9d92e99f5144bd74611&quot;,</span><br><span class="line">    &quot;confirmations&quot; : 144667,</span><br><span class="line">    &quot;size&quot; : 1536,</span><br><span class="line">    &quot;height&quot; : 116219,</span><br><span class="line">    &quot;version&quot; : 1,</span><br><span class="line">    &quot;merkleroot&quot; : &quot;587fefd748f899f84d0fa1d8a3876fdb406a4bb8f54a31445cb72564701daea6&quot;,</span><br><span class="line">    &quot;tx&quot; : [</span><br><span class="line">        &quot;be8f08d7f519eb863a68cf292ca51dbab7c9b49f50a96d13f2db32e432db363e&quot;,</span><br><span class="line">        &quot;a387039eca66297ba51ef2da3dcc8a0fc745bcb511e20ed9505cc6762be037bb&quot;,</span><br><span class="line">        &quot;2bd83162e264abf59f9124ca517050065f8c8eed2a21fbf85d454ee4e0e4c267&quot;,</span><br><span class="line">        &quot;028cfae228f8a4b0caee9c566bd41aed36bcd237cdc0eb18f0331d1e87111743&quot;,</span><br><span class="line">        &quot;3a06b6615756dc3363a8567fbfa8fe978ee0ba06eb33fd844886a0f01149ad62&quot;</span><br><span class="line">    ],</span><br><span class="line">    &quot;time&quot; : 1301705313,</span><br><span class="line">    &quot;nonce&quot; : 1826107553,</span><br><span class="line">    &quot;bits&quot; : &quot;1b00f339&quot;,</span><br><span class="line">    &quot;difficulty&quot; : 68977.78463021,</span><br><span class="line">    &quot;previousblockhash&quot; : &quot;00000000000010d549135eb39bd3bbb1047df8e1512357216e8a85c57a1efbfb&quot;,</span><br><span class="line">    &quot;nextblockhash&quot; : &quot;000000000000e9fcc59a6850f64a94476a30f5fe35d6d8c4b4ce0b1b04103a77&quot;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>该Block里面有5笔交易，第一笔为Generation TX，解析出来看一下具体内容：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"># ~  bitcoind getrawtransaction be8f08d7f519eb863a68cf292ca51dbab7c9b49f50a96d13f2db32e432db363e 1</span><br><span class="line">&#123;</span><br><span class="line">    &quot;hex&quot; : &quot;01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff070439f3001b0134ffffffff014034152a010000004341045b3aaa284d169c5ae2d20d0b0673468ed3506aa8fea5976eacaf1ff304456f6522fbce1a646a24005b8b8e771a671f564ca6c03e484a1c394bf96e2a4ad01dceac00000000&quot;,</span><br><span class="line">    &quot;txid&quot; : &quot;be8f08d7f519eb863a68cf292ca51dbab7c9b49f50a96d13f2db32e432db363e&quot;,</span><br><span class="line">    &quot;version&quot; : 1,</span><br><span class="line">    &quot;locktime&quot; : 0,</span><br><span class="line">    &quot;vin&quot; : [</span><br><span class="line">        &#123;</span><br><span class="line">            &quot;coinbase&quot; : &quot;0439f3001b0134&quot;,</span><br><span class="line">            &quot;sequence&quot; : 4294967295</span><br><span class="line">        &#125;</span><br><span class="line">    ],</span><br><span class="line">    &quot;vout&quot; : [</span><br><span class="line">        &#123;</span><br><span class="line">            &quot;value&quot; : 50.01000000,</span><br><span class="line">            &quot;n&quot; : 0,</span><br><span class="line">            &quot;scriptPubKey&quot; : &#123;</span><br><span class="line">                &quot;asm&quot; : &quot;045b3aaa284d169c5ae2d20d0b0673468ed3506aa8fea5976eacaf1ff304456f6522fbce1a646a24005b8b8e771a671f564ca6c03e484a1c394bf96e2a4ad01dce OP_CHECKSIG&quot;,</span><br><span class="line">                &quot;hex&quot; : &quot;41045b3aaa284d169c5ae2d20d0b0673468ed3506aa8fea5976eacaf1ff304456f6522fbce1a646a24005b8b8e771a671f564ca6c03e484a1c394bf96e2a4ad01dceac&quot;,</span><br><span class="line">                &quot;reqSigs&quot; : 1,</span><br><span class="line">                &quot;type&quot; : &quot;pubkey&quot;,</span><br><span class="line">                &quot;addresses&quot; : [</span><br><span class="line">                    &quot;1LgZTvoTJ6quJNCURmBUaJJkWWQZXkQnDn&quot;</span><br><span class="line">                ]</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    ],</span><br><span class="line">    &quot;blockhash&quot; : &quot;0000000000007c639f2cbb23e4606a1d022fa4206353b9d92e99f5144bd74611&quot;,</span><br><span class="line">    &quot;confirmations&quot; : 145029,</span><br><span class="line">    &quot;time&quot; : 1301705313,</span><br><span class="line">    &quot;blocktime&quot; : 1301705313</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>Generation TX的输入不是一个交易，而带有<code>coinbase</code>字段的结构。该字段的值由挖出此Block的人填写，这是一种“特权”：可以把信息写入货币系统（大家很喜欢用系统中的数据结构字段名来命名站点，例如blockchain、coinbase等，这些词的各种后缀域名都被抢注一空）。中本聪在比特币的第一个交易中的写入的<code>coinbase</code>值是：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&quot;coinbase&quot;:&quot;04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73&quot;</span><br></pre></td></tr></table></figure><p>将该段16进制转换为ASCII字符，就是那段著名的创世块留言：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">The Times 03/Jan/2009 Chancellor on brink of second bailout for banks</span><br></pre></td></tr></table></figure><p>接下来展示的是一个三个输入、两个输出的普通交易：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br></pre></td><td class="code"><pre><span class="line"># ~  bitcoind getrawtransaction 028cfae228f8a4b0caee9c566bd41aed36bcd237cdc0eb18f0331d1e87111743 1</span><br><span class="line">&#123;</span><br><span class="line">    &quot;hex&quot; : &quot;0100000003c9f3b07ebfca68fd1a6339d0808fbb013c90c6095fc93901ea77410103489ab7000000008a473044022055bac1856ecbc377dd5e869b1a84ed1d5228c987b098c095030c12431a4d5249022055523130a9d0af5fc27828aba43b464ecb1991172ba2a509b5fbd6cac97ff3af0141048aefd78bba80e2d1686225b755dacea890c9ca1be10ec98173d7d5f2fefbbf881a6e918f3b051f8aaaa3fcc18bbf65097ce8d30d5a7e5ef8d1005eaafd4b3fbeffffffffc9f3b07ebfca68fd1a6339d0808fbb013c90c6095fc93901ea77410103489ab7010000008a47304402206b993231adec55e6085e75f7dc5ca6c19e42e744cd60abaff957b1c352b3ef9a022022a22fec37dfa2c646c78d9a0753d56cb4393e8d0b22dc580ef1aa6cccef208d0141042ff65bd6b3ef04253225405ccc3ab2dd926ff2ee48aac210819698440f35d785ec3cec92a51330eb0c76cf49e9e474fb9159ab41653a9c1725c031449d31026affffffffc98620a6c40fc7b3a506ad79af339541762facd1dd80ff0881d773fb72b230da010000008b483045022040a5d957e087ed61e80f1110bcaf4901b5317c257711a6cbc54d6b98b6a8563f02210081e3697031fe82774b8f44dd3660901e61ac5a99bff2d0efc83ad261da5b4f1d014104a7d1a57e650613d3414ebd59e3192229dc09d3613e547bdd1f83435cc4ca0a11c679d96456cae75b1f5563728ec7da1c1f42606db15bf554dbe8a829f3a8fe2fffffffff0200bd0105000000001976a914634228c26cf40a02a05db93f2f98b768a8e0e61b88acc096c7a6030000001976a9147514080ab2fcac0764de3a77d10cb790c71c74c288ac00000000&quot;,</span><br><span class="line">    &quot;txid&quot; : &quot;028cfae228f8a4b0caee9c566bd41aed36bcd237cdc0eb18f0331d1e87111743&quot;,</span><br><span class="line">    &quot;version&quot; : 1,</span><br><span class="line">    &quot;locktime&quot; : 0,</span><br><span class="line">    &quot;vin&quot; : [</span><br><span class="line">        &#123;</span><br><span class="line">            &quot;txid&quot; : &quot;b79a4803014177ea0139c95f09c6903c01bb8f80d039631afd68cabf7eb0f3c9&quot;,</span><br><span class="line">            &quot;vout&quot; : 0,</span><br><span class="line">            &quot;scriptSig&quot; : &#123;</span><br><span class="line">                &quot;asm&quot; : &quot;3044022055bac1856ecbc377dd5e869b1a84ed1d5228c987b098c095030c12431a4d5249022055523130a9d0af5fc27828aba43b464ecb1991172ba2a509b5fbd6cac97ff3af01 048aefd78bba80e2d1686225b755dacea890c9ca1be10ec98173d7d5f2fefbbf881a6e918f3b051f8aaaa3fcc18bbf65097ce8d30d5a7e5ef8d1005eaafd4b3fbe&quot;,</span><br><span class="line">                &quot;hex&quot; : &quot;473044022055bac1856ecbc377dd5e869b1a84ed1d5228c987b098c095030c12431a4d5249022055523130a9d0af5fc27828aba43b464ecb1991172ba2a509b5fbd6cac97ff3af0141048aefd78bba80e2d1686225b755dacea890c9ca1be10ec98173d7d5f2fefbbf881a6e918f3b051f8aaaa3fcc18bbf65097ce8d30d5a7e5ef8d1005eaafd4b3fbe&quot;</span><br><span class="line">            &#125;,</span><br><span class="line">            &quot;sequence&quot; : 4294967295</span><br><span class="line">        &#125;,</span><br><span class="line">        &#123;</span><br><span class="line">            &quot;txid&quot; : &quot;b79a4803014177ea0139c95f09c6903c01bb8f80d039631afd68cabf7eb0f3c9&quot;,</span><br><span class="line">            &quot;vout&quot; : 1,</span><br><span class="line">            &quot;scriptSig&quot; : &#123;</span><br><span class="line">                &quot;asm&quot; : &quot;304402206b993231adec55e6085e75f7dc5ca6c19e42e744cd60abaff957b1c352b3ef9a022022a22fec37dfa2c646c78d9a0753d56cb4393e8d0b22dc580ef1aa6cccef208d01 042ff65bd6b3ef04253225405ccc3ab2dd926ff2ee48aac210819698440f35d785ec3cec92a51330eb0c76cf49e9e474fb9159ab41653a9c1725c031449d31026a&quot;,</span><br><span class="line">                &quot;hex&quot; : &quot;47304402206b993231adec55e6085e75f7dc5ca6c19e42e744cd60abaff957b1c352b3ef9a022022a22fec37dfa2c646c78d9a0753d56cb4393e8d0b22dc580ef1aa6cccef208d0141042ff65bd6b3ef04253225405ccc3ab2dd926ff2ee48aac210819698440f35d785ec3cec92a51330eb0c76cf49e9e474fb9159ab41653a9c1725c031449d31026a&quot;</span><br><span class="line">            &#125;,</span><br><span class="line">            &quot;sequence&quot; : 4294967295</span><br><span class="line">        &#125;,</span><br><span class="line">        &#123;</span><br><span class="line">            &quot;txid&quot; : &quot;da30b272fb73d78108ff80ddd1ac2f76419533af79ad06a5b3c70fc4a62086c9&quot;,</span><br><span class="line">            &quot;vout&quot; : 1,</span><br><span class="line">            &quot;scriptSig&quot; : &#123;</span><br><span class="line">                &quot;asm&quot; : &quot;3045022040a5d957e087ed61e80f1110bcaf4901b5317c257711a6cbc54d6b98b6a8563f02210081e3697031fe82774b8f44dd3660901e61ac5a99bff2d0efc83ad261da5b4f1d01 04a7d1a57e650613d3414ebd59e3192229dc09d3613e547bdd1f83435cc4ca0a11c679d96456cae75b1f5563728ec7da1c1f42606db15bf554dbe8a829f3a8fe2f&quot;,</span><br><span class="line">                &quot;hex&quot; : &quot;483045022040a5d957e087ed61e80f1110bcaf4901b5317c257711a6cbc54d6b98b6a8563f02210081e3697031fe82774b8f44dd3660901e61ac5a99bff2d0efc83ad261da5b4f1d014104a7d1a57e650613d3414ebd59e3192229dc09d3613e547bdd1f83435cc4ca0a11c679d96456cae75b1f5563728ec7da1c1f42606db15bf554dbe8a829f3a8fe2f&quot;</span><br><span class="line">            &#125;,</span><br><span class="line">            &quot;sequence&quot; : 4294967295</span><br><span class="line">        &#125;</span><br><span class="line">    ],</span><br><span class="line">    &quot;vout&quot; : [</span><br><span class="line">        &#123;</span><br><span class="line">            &quot;value&quot; : 0.84000000,</span><br><span class="line">            &quot;n&quot; : 0,</span><br><span class="line">            &quot;scriptPubKey&quot; : &#123;</span><br><span class="line">                &quot;asm&quot; : &quot;OP_DUP OP_HASH160 634228c26cf40a02a05db93f2f98b768a8e0e61b OP_EQUALVERIFY OP_CHECKSIG&quot;,</span><br><span class="line">                &quot;hex&quot; : &quot;76a914634228c26cf40a02a05db93f2f98b768a8e0e61b88ac&quot;,</span><br><span class="line">                &quot;reqSigs&quot; : 1,</span><br><span class="line">                &quot;type&quot; : &quot;pubkeyhash&quot;,</span><br><span class="line">                &quot;addresses&quot; : [</span><br><span class="line">                    &quot;1A3q9pDtR4h8wpvyb8SVpiNPpT8ZNbHY8h&quot;</span><br><span class="line">                ]</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;,</span><br><span class="line">        &#123;</span><br><span class="line">            &quot;value&quot; : 156.83000000,</span><br><span class="line">            &quot;n&quot; : 1,</span><br><span class="line">            &quot;scriptPubKey&quot; : &#123;</span><br><span class="line">                &quot;asm&quot; : &quot;OP_DUP OP_HASH160 7514080ab2fcac0764de3a77d10cb790c71c74c2 OP_EQUALVERIFY OP_CHECKSIG&quot;,</span><br><span class="line">                &quot;hex&quot; : &quot;76a9147514080ab2fcac0764de3a77d10cb790c71c74c288ac&quot;,</span><br><span class="line">                &quot;reqSigs&quot; : 1,</span><br><span class="line">                &quot;type&quot; : &quot;pubkeyhash&quot;,</span><br><span class="line">                &quot;addresses&quot; : [</span><br><span class="line">                    &quot;1Bg44FZsoTeYteRykC1XHz8facWYKhGvQ8&quot;</span><br><span class="line">                ]</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    ],</span><br><span class="line">    &quot;blockhash&quot; : &quot;0000000000007c639f2cbb23e4606a1d022fa4206353b9d92e99f5144bd74611&quot;,</span><br><span class="line">    &quot;confirmations&quot; : 147751,</span><br><span class="line">    &quot;time&quot; : 1301705313,</span><br><span class="line">    &quot;blocktime&quot; : 1301705313</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>字段<code>hex</code>记录了所有相关信息，后面显示的是<code>hex</code>解析出来的各类字段信息。下面把逐个分解<code>hex</code>内容（hex可以从上面的直接看到）：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line">01000000   // 版本号，UINT32</span><br><span class="line">03         // Tx输入数量，变长INT。3个输入。</span><br><span class="line"></span><br><span class="line">/*** 第一组Input Tx ***/</span><br><span class="line">// Tx Hash，固定32字节</span><br><span class="line">c9f3b07ebfca68fd1a6339d0808fbb013c90c6095fc93901ea77410103489ab7</span><br><span class="line">00000000  // 消费的Tx位于前向交易输出的第0个，UINT32，固定4字节</span><br><span class="line">8a        // 签名的长度, 0x8A = 138字节</span><br><span class="line">// 138字节长度的签名，含有两个部分：公钥+签名</span><br><span class="line">47       // 签名长度，0x47 = 71字节</span><br><span class="line">3044022055bac1856ecbc377dd5e869b1a84ed1d5228c987b098c095030c12431a4d5249022055523130a9d0af5fc27828aba43b464ecb1991172ba2a509b5fbd6cac97ff3af01</span><br><span class="line">41       // 公钥长度，0x41 = 65字节</span><br><span class="line">048aefd78bba80e2d1686225b755dacea890c9ca1be10ec98173d7d5f2fefbbf881a6e918f3b051f8aaaa3fcc18bbf65097ce8d30d5a7e5ef8d1005eaafd4b3fbe</span><br><span class="line">ffffffff  // sequence，0xffffffff = 4294967295， UINT32, 固定4字节</span><br><span class="line"></span><br><span class="line">/*** 第二组Input Tx。与上同理，省略分解 ***/</span><br><span class="line">c9f3b07ebfca68fd1a6339d0808fbb013c90c6095fc93901ea77410103489ab7010000008a47304402206b993231adec55e6085e75f7dc5ca6c19e42e744cd60abaff957b1c352b3ef9a022022a22fec37dfa2c646c78d9a0753d56cb4393e8d0b22dc580ef1aa6cccef208d0141042ff65bd6b3ef04253225405ccc3ab2dd926ff2ee48aac210819698440f35d785ec3cec92a51330eb0c76cf49e9e474fb9159ab41653a9c1725c031449d31026affffffff</span><br><span class="line"></span><br><span class="line">/*** 第三组Input Tx ***/</span><br><span class="line">c98620a6c40fc7b3a506ad79af339541762facd1dd80ff0881d773fb72b230da010000008b483045022040a5d957e087ed61e80f1110bcaf4901b5317c257711a6cbc54d6b98b6a8563f02210081e3697031fe82774b8f44dd3660901e61ac5a99bff2d0efc83ad261da5b4f1d014104a7d1a57e650613d3414ebd59e3192229dc09d3613e547bdd1f83435cc4ca0a11c679d96456cae75b1f5563728ec7da1c1f42606db15bf554dbe8a829f3a8fe2fffffffff</span><br><span class="line"></span><br><span class="line">02  // Tx输出数量，变长INT。两个输出。</span><br><span class="line"></span><br><span class="line">/*** 第一组输出 ***/</span><br><span class="line">00bd010500000000    // 输出的币值，UINT64，8个字节。字节序需翻转，~= 0x000000000501bd00 = 84000000 satoshi</span><br><span class="line">19                  // 输出目的地址字节数, 0x19 = 25字节，由一些操作码与数值构成</span><br><span class="line">// 目标地址</span><br><span class="line">// 0x76 -&gt; OP_DUP(stack ops)</span><br><span class="line">// 0xa9 -&gt; OP_HASH160(crypto)</span><br><span class="line">// 0x14 -&gt; 长度，0x14 = 20字节</span><br><span class="line">76 a9 14 </span><br><span class="line">// 地址的HASH160值，20字节</span><br><span class="line">634228c26cf40a02a05db93f2f98b768a8e0e61b </span><br><span class="line">// 0x88 -&gt; OP_EQUALVERIFY(bit logic)</span><br><span class="line">// 0xac -&gt; OP_CHECKSIG(crypto)</span><br><span class="line">88 ac</span><br><span class="line"></span><br><span class="line">/*** 第二组输出 ***/</span><br><span class="line">c096c7a603000000</span><br><span class="line">19</span><br><span class="line">76 a9 14 7514080ab2fcac0764de3a77d10cb790c71c74c2 88 ac</span><br><span class="line"></span><br><span class="line">00000000  // lock_time，UINT32，固定4字节</span><br></pre></td></tr></table></figure><p>Tx Hash，俗称交易ID，由<code>hex</code>得出：<code>Tx Hash = SHA256(SHA256(hex))</code>。由于每个交易只能成为下一个的输入，有且仅有一次，那么不存在输入完全相同的交易，那么就不存在相同的Tx Hash（SHA256碰撞概率极小，所以无需考虑Hash碰撞的问题，就像无需考虑地址私钥被别人撞到一样）。</p><p>即便如此，在系统里依然产生了相同的Tx Hash，是某位矿工兄弟挖出Block后，打包Block时忘记修改Generation Tx coinbase字段的值，币量相同且输出至相同的地址，那么就构造了两个完全一模一样的交易，分别位于两个Block的第一个位置。这个对系统不会产生什么问题，但只要花费其中一笔，另一个也被花费了。相同的Generation Tx相当于覆盖了另一个，白白损失了挖出的币。该交易ID为<a href="https://blockchain.info/tx/e3bf3d07d4b0375638d5f1db5255fe07ba2c4cb067cd81b84ee974b6585fb468" title="e3bf3d07d4b0375638d5f1db5255fe07ba2c4cb067cd81b84ee974b6585fb468">e3bf3d07d4b0375638d5f1db5255fe07ba2c4cb067cd81b84ee974b6585fb468</a>，第一次出现在<a href="https://blockchain.info/block/00000000000271a2dc26e7667f8419f2e15416dc6955e5a6c6cdf3f2574dd08e">#91722</a>，第二次出现在<a href="https://blockchain.info/block/00000000000743f190a18c5577a3c2d2a1f610ae9601ac046a38084ccb7cd721">#91880</a>。</p><p><img src="https://f.cloud.github.com/assets/514951/1415138/b01dc7da-3edf-11e3-86eb-d015037e9440.png" alt="qq20131027-2"></p><h2 id="交易签名"><a href="#交易签名" class="headerlink" title="交易签名"></a>交易签名</h2><p>签名是对所有权的验证，节点收到交易广播后，会对交易进行验证，通过后则收录进内存、打包进Block，否则，丢弃之。签名就类似传统纸质合同盖章、签字过程，合法转移所有权的保证手段。</p><h3 id="签名类型"><a href="#签名类型" class="headerlink" title="签名类型"></a>签名类型</h3><p>由于一个交易的输入、输出都可能具有多个，那么签名也具有多种类型，目前共三类：SIGHASH_ALL, SIGHASH_NONE, SIGHASH_SINGLE。</p><h4 id="SIGHASH-ALL"><a href="#SIGHASH-ALL" class="headerlink" title="SIGHASH_ALL"></a>SIGHASH_ALL</h4><p>该签名类型为默认类型，也是目前绝大部分交易采用的，顾名思义即签名整单交易。首先，组织所有输出、输入，就像上文分解Hex过程一样，每个输入都对应一个签名，暂时留空，其他包括sequence等字段均须填写，这样就形成了一个完整的交易Hex（只缺签名字段）。然后，每一个输入均需使用私钥对该段数据进行签名，签名完成后各自填入相应的位置，N个输入N个签名。简单理解就是：对于该笔单子，认可且只认可的这些输入、输出，并同意花费我的那笔输入。</p><h4 id="SIGHASH-NONE"><a href="#SIGHASH-NONE" class="headerlink" title="SIGHASH_NONE"></a>SIGHASH_NONE</h4><p>该签名类型是最自由松散的，仅对输入签名，不对输出签名，输出可以任意指定。某人对某笔币签名后交给你，你可以在任意时刻填入任意接受地址，广播出去令其生效。简单理解就是：我同意花费我的那笔钱，至于给谁，我不关心。</p><h4 id="SIGHASH-SINGLE"><a href="#SIGHASH-SINGLE" class="headerlink" title="SIGHASH_SINGLE"></a>SIGHASH_SINGLE</h4><p>该签名类型其次自由松散，仅对自己的输入、输出签名，并留空sequence字段。其输入的次序对应其输出的次序，比如输入是第3个，那么签名的输出也是第三个。简单理解就是：我同意花费我的那笔钱，且只能花费到我认可的输出，至于单子里的其他输入、输出，我不关心。</p>]]>
    </content>
    <id>https://kevinpan.page/2013/10/27/bitcoin-transaction-01/</id>
    <link href="https://kevinpan.page/2013/10/27/bitcoin-transaction-01/"/>
    <published>2013-10-28T00:42:00.000Z</published>
    <summary>
      <![CDATA[<h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><p>交易(Transaction)是比特币系统的信息载体，最小单元。而块(Block)就是将这些基础单元打包装箱，贴上封条，并串联起来。巨大算力]]>
    </summary>
    <title>比特币交易构成（一）</title>
    <updated>2013-10-28T00:42:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Kevin Pan</name>
      <email>bit.kevin@gmail.com</email>
    </author>
    <content>
      <![CDATA[<h3 id="存在证明"><a href="#存在证明" class="headerlink" title="存在证明"></a>存在证明</h3><p>存在证明就是向第三方证明某个物品&#x2F;事件，在过去的某个时刻存在过。</p><p>这是一件很简单的事情，提供票据、通信记录之类的就可以办到。但这些并不严格，因为这些证据都是非常易伪造或销毁。要完成证明，必须依赖强有力的证据链，这个必须是任何人都无法伪造与销毁的，或者说伪造成本极其高昂近乎不可能。</p><p>回忆一下，电影里经常出现的绑匪镜头，他们为了证明在某个时间确实拥有人质，而不是事前拍摄的视频，通常会用当天的发行量很大的报纸来辅助证明。当香港媒体误报“成龙高楼坠亡”时，成龙也不得不拿报纸来证明自己的存在：</p><p><img src="https://f.cloud.github.com/assets/514951/906787/0f6c4fcc-fcdc-11e2-8770-b72819e92915.jpg" alt="124897097_11n"></p><p>报纸之所以能够成为有效的时间证明系统是因为：</p><ol><li>不可伪造性。新闻等信息是无法预测的，尤其是证券大盘数据，报纸上大量充满这样的信息，所以无人能够提前伪造。</li><li>公开且不可销毁。报纸通常拥有很大的发行数量，受众广泛，一旦发布出去就分散到各个角落，很难再次收集齐全并全部销毁。通常图书馆也会存档数十年期限的报纸。</li><li>具有时间特征。报纸具有很强时间特征，版面到处可见的是时间标记。</li></ol><p>借助报纸可以完成某个时间之后的存在证明，但无法完成某个时间之前的。例如，你拿9月1号的报纸拍摄进照片，那么仅能证明其在9月1号之后拍摄，可能是9月1号，也可能是9月15号。</p><h3 id="时间戳服务"><a href="#时间戳服务" class="headerlink" title="时间戳服务"></a>时间戳服务</h3><p><code>比特币本质是构造了一个永不停息、无坚不摧的时间戳系统</code>。</p><p><img src="https://f.cloud.github.com/assets/514951/906809/5281de94-fce1-11e2-83be-e80533cae1e9.png" alt="qq20130804-9"></p><p>然后该系统上添加若干特性后使得具有货币的功能。报纸从另一个角度讲也是一种时间戳服务。</p><p>比特币具有下列优良的特性可以更完美的用于存在证明：</p><ul><li>不可预测&#x2F;伪造。因block的计算是随机事件，其hash值是一个32字节的随机大数(2^256)。想蒙对该数的概率实在是太低了。</li><li>不可销毁&#x2F;修改。Block Chain拥有巨大的算力在维护与延续，对于N个确认的block，想篡改是不可能的。</li><li>block具有天然时间特性。timestamp是block meta字段之一。</li><li>block可以存储信息。对于block meta信息，是无法控制的。但block会收录交易，而交易是可以”写入”自己的数据。</li></ul><h3 id="数字摘要"><a href="#数字摘要" class="headerlink" title="数字摘要"></a>数字摘要</h3><p>简单来说，对一串数据进行Hash运算，得到的Hash值称为数字摘要。除了Hash函数，还有其他方式，如密钥签名等也可以得到。Hash值通常是一个非常巨大的数，例如用SHA256时，Hash值区间非常大：1~2^256。数字摘要的计算过程不可逆，那么可以认为：</p><pre><code>欲证明你拥有某个文件，提供该文件的Hash值（及Hash函数）即可。第三方可以轻易验证文件的Hash值来判断之。</code></pre><h3 id="比特币做存在证明"><a href="#比特币做存在证明" class="headerlink" title="比特币做存在证明"></a>比特币做存在证明</h3><h4 id="时间点后向证明"><a href="#时间点后向证明" class="headerlink" title="时间点后向证明"></a>时间点后向证明</h4><p>因为block hash的不可伪造性，能提供Block Hash即可证明存在于该Block时刻之后。例如，你在拍照的时候，拿着打印有block hash的纸即可证明：你在该block时刻之后进行的拍摄。</p><h4 id="时间点前向证明"><a href="#时间点前向证明" class="headerlink" title="时间点前向证明"></a>时间点前向证明</h4><p>前向证明需要精心构造一个包含数字摘要的交易，待该交易进入block中。便可以证明你在该block时刻之前拥有该数字摘要。<code>前向证明的关键是能把信息写入时间戳服务载体</code>。</p><h4 id="时间区间证明"><a href="#时间区间证明" class="headerlink" title="时间区间证明"></a>时间区间证明</h4><p>有时候，仅仅证明时间点之前或之后是不够的，需要能够确认到某一个时刻。将上述方式综合即可完成：</p><ol><li>将block A的hash值添入数据文件，并制作文件数字摘要。（时间点后向证明）</li><li>将摘要信息构造至交易中，广播之。（时间点前向证明）</li><li>当交易被block B收录进去，那么即可证明，该文件于block A与B的时间间隔中存在。</li></ol><p>如果交易给了足够的矿工费(Transaction Fee)，具有较高优先级的话，便很有可能被紧随其后的block收录。连续的block约10分钟，那么就在一个相对小的时间内作了证明，可以近似认为是时间点。</p><h3 id="构造特殊交易"><a href="#构造特殊交易" class="headerlink" title="构造特殊交易"></a>构造特殊交易</h3><p>带有数字摘要的交易如何构造呢？下面以32字节的数字摘要为例，提出数个可行方法，其他长度的可变换得出。</p><h4 id="方式一：交易额承载信息"><a href="#方式一：交易额承载信息" class="headerlink" title="方式一：交易额承载信息"></a>方式一：交易额承载信息</h4><p>32字节可以分割为16个双字节，每个双字节的数值范围是：0~65535。比特币的现行单位可以分割至小数点后八位，那么我们可以利用最后的5位来存放一个数值，一共需要16个输出(Tx output)即可完成32字节的信息存储。中间涉及比特币最大数量为: </p><pre><code>.00065535 * 16 = .0104856 btc</code></pre><p>需要的比特币数量很少，约0.01Btc，且输出依然发回给自己的地址，唯一的代价就是付出矿工费(Tx Fee)。任何人都可以使用之。</p><h5 id="SatoshiDice种子文件时间证明"><a href="#SatoshiDice种子文件时间证明" class="headerlink" title="SatoshiDice种子文件时间证明"></a>SatoshiDice种子文件时间证明</h5><p>著名站点SatoshiDice就是采用这种方式为其服务端种子文件做时间前向证明的。下面演示一下步骤。服务端的种子文件为<code>hash.keys</code>，我们对其做SHA256运算，得到hash值，32个字节。</p><pre><code>$ sha256sum hash.keys# hash of file &quot;hash.keys&quot;, in hex:9b0d87ac871518cfd8601aa456b58fa74c01194cfeb25e7f3eecf43759d6ccb4  hash.keys</code></pre><p>将该hash转为16个10进制数值：</p><pre><code>9b0d = 3969387ac = 347328715 = 3458118cf = 6351d860 = 553921aa4 = 682056b5 = 221978fa7 = 367754c01 = 19457194c = 6476feb2 = 652025e7f = 241913eec = 16108f437 = 6251959d6 = 22998ccb4 = 52404</code></pre><p>将这16个数除以10^8，作为输出额度，构造<a href="http://blockchain.info/tx/428bcc630b00fe431623b4e1fb0f726493dc0a2ead86ace9f65cd51bc8092459">交易</a>：</p><p><img src="https://f.cloud.github.com/assets/514951/947402/555dc5de-0352-11e3-8cf9-c7d8de72adbe.png" alt="qq20130812-2"></p><p>交易被收录，证明完成。</p><h4 id="方式二：数字摘要的Hash作地址输入"><a href="#方式二：数字摘要的Hash作地址输入" class="headerlink" title="方式二：数字摘要的Hash作地址输入"></a>方式二：数字摘要的Hash作地址输入</h4><p>回顾一下地址的生成算法（下图是一个未压缩公钥生成地址的过程，公钥是否压缩对该证明过程没有影响）：</p><p><img src="https://f.cloud.github.com/assets/514951/947559/47602d38-0356-11e3-8fa7-ba59e99eee85.png" alt="qq20130812-3"></p><p>我们用数字摘要的Hash值代替图中红色框中的值，然后得到一个地址，我们把0.00000001 btc打入该地址，形成交易，收录后完成证明。验证时，需要首先得到数字摘要hash值，再生成对应的地址，核对地址是否一致即可。</p><p>这个方法有个缺点，打入该地址的币永远消失了，因为没有其对应的私钥。虽然可以只需1聪，目前价值几乎忽略不计，但毕竟浪费了。该方法可以进一步衍生一些类似的方法。曾有个网站使用之，后来该网站关闭了。</p><h4 id="方式三：数字摘要的Hash作私钥"><a href="#方式三：数字摘要的Hash作私钥" class="headerlink" title="方式三：数字摘要的Hash作私钥"></a>方式三：数字摘要的Hash作私钥</h4><p>大小介于<code>1 ~ 0xFFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFE BAAE DCE6 AF48 A03B BFD2 5E8C D036 4141</code>之间的数，都可以认为是一个合法的私钥，其大小为32字节。那么，可以把数字摘要的Hash作私钥，并推算出公钥和地址。</p><p>将任意币值输出至该地址构成交易，交易收录后，通过私钥再转移走即可。这样便在block chain里留下了这个地址。验证时重复该过程，检查地址是否一致即可。</p><p>该方法不会像方法二那样形成浪费，也比较容易操作。我们依然SatoshiDice的种子文件为例，种子数字摘要的Hash为：<code>9b0d87ac871518cfd8601aa456b58fa74c01194cfeb25e7f3eecf43759d6ccb4</code>。</p><p>借助<a href="https://www.bitaddress.org/">bitaddress.org</a>，输入私钥（种子hash）后：</p><p><img src="https://f.cloud.github.com/assets/514951/947730/30cc9c38-035a-11e3-98da-590a87880173.png" alt="qq20130812-5"></p><p>得到两把公钥，分别对应两个地址。证明时将币打入任何一个地址即可，建议使用未压缩公钥地址，因为并不是所有客户端都对压缩公钥支持良好。然后将该私钥导入任何一个客户端，再把该地址的钱转移到一个安全的地方。最后，公开私钥和对应收录地址的交易。</p><h4 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h4><p>方法二浪费，应避免使用。方法一繁琐，需要工具辅助转换。方法三相对容易，门槛低一些，大部分客户端都支持，私钥公钥地址的推导也有很多工具支持。</p><p>就这样比特币系统轻松的完成了存在证明，安全稳固，公信力远胜任何第三方、机构、政府。过程极其简单，使得任何一人都可以轻易地做出存在证明，其意义非常重大。可以预见，未来将比特币作为存在证明会得到广泛的应用。</p>]]>
    </content>
    <id>https://kevinpan.page/2013/08/12/timestamp-system-and-proof-of-existance/</id>
    <link href="https://kevinpan.page/2013/08/12/timestamp-system-and-proof-of-existance/"/>
    <published>2013-08-13T03:53:00.000Z</published>
    <summary>
      <![CDATA[<h3 id="存在证明"><a href="#存在证明" class="headerlink" title="存在证明"></a>存在证明</h3><p>存在证明就是向第三方证明某个物品&#x2F;事件，在过去的某个时刻存在过。</p>
<p>这是一件很简单的事情，提供票据、通]]>
    </summary>
    <title>时间戳服务与存在证明</title>
    <updated>2013-08-13T03:53:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Kevin Pan</name>
      <email>bit.kevin@gmail.com</email>
    </author>
    <content>
      <![CDATA[<h3 id="工作证明"><a href="#工作证明" class="headerlink" title="工作证明"></a>工作证明</h3><p>工作证明(Proof Of Work，简称POW)，顾名思义，即工作量的证明。通常来说只能从结果证明，因为监测工作过程通常是繁琐与低效的。</p><p>比特币在Block的生成过程中使用了POW机制，一个符合要求的Block Hash由N个前导零构成，零的个数取决于网络的难度值。要得到合理的Block Hash需要经过大量尝试计算，计算时间取决于机器的哈希运算速度。当某个节点提供出一个合理的Block Hash值，说明该节点确实经过了大量的尝试计算，当然，并不能得出计算次数的绝对值，因为寻找合理hash是一个概率事件。当节点拥有占全网n%的算力时，该节点即有n&#x2F;100的概率找到Block Hash。</p><p>工作证明机制看似很神秘，其实在社会中的应用非常广泛。例如，毕业证、学位证等证书，就是工作证明，拥有证书即表明你在过去投入了学习与工作。生活大部分事情都是通过结果来判断的。</p><h3 id="挖矿"><a href="#挖矿" class="headerlink" title="挖矿"></a>挖矿</h3><p>挖矿即不断接入新的Block延续Block Chain的过程。</p><p><img src="https://f.cloud.github.com/assets/514951/886819/d2df8f62-f9f0-11e2-93da-4f66e3093c33.png" alt="blockchain"></p><p>挖矿为整个系统的运转提供原动力，是比特币的发动机，<code>没有挖矿就没有比特币</code>。挖矿有三个重要功能：</p><ol><li>发行新的货币（总量达到之前）</li><li>维系货币的支付功能</li><li>通过算力保障系统安全</li></ol><p>金矿消耗资源将黄金注入流通经济，比特币通过“挖矿”完成相同的事情，只不过消耗的是CPU时间与电力。当然，比特币的挖矿意义远大于此。</p><h4 id="Block-Hash算法"><a href="#Block-Hash算法" class="headerlink" title="Block Hash算法"></a>Block Hash算法</h4><p>Block头部信息的构成：</p><table><thead><tr><th align="left">字段名</th><th>含义</th><th align="center">大小(字节)</th></tr></thead><tbody><tr><td align="left">Version</td><td>版本号</td><td align="center">4</td></tr><tr><td align="left">hashPrevBlock</td><td>上一个block hash值</td><td align="center">32</td></tr><tr><td align="left">hashMerkleRoot</td><td>上一个block产生之后至新block生成此时间内，<br/>交易数据打包形成的Hash</td><td align="center">32</td></tr><tr><td align="left">Time</td><td>Unix时间戳</td><td align="center">4</td></tr><tr><td align="left">Bits</td><td>目标值，即难度</td><td align="center">4</td></tr><tr><td align="left">Nonce</td><td>随机数</td><td align="center">4</td></tr></tbody></table><p>下面采用高度为<a href="http://blockexplorer.com/rawblock/00000000000000001e8d6829a8a21adc5d38d0a473b144b6765798e61f98bd1d">125552</a>的block数据为例，演示block hash的计算过程：</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span>                                                                                                                             </span><br><span class="line"><span class="variable">$header_hex</span> = <span class="string">&quot;01000000&quot;</span> . <span class="comment">// version</span></span><br><span class="line">              <span class="comment">// previous block hash</span></span><br><span class="line">              <span class="string">&quot;81cd02ab7e569e8bcd9317e2fe99f2de44d49ab2b8851ba4a308000000000000&quot;</span> .</span><br><span class="line">              <span class="comment">// merkle root hash of transactions in this block</span></span><br><span class="line">              <span class="string">&quot;e320b6c2fffc8d750423db8b1eb942ae710e951ed797f7affc8892b0f1fc122b&quot;</span> .</span><br><span class="line">              <span class="comment">// Time</span></span><br><span class="line">              <span class="string">&quot;c7f5d74d&quot;</span> .</span><br><span class="line">              <span class="comment">// Bits (Difficulty)</span></span><br><span class="line">              <span class="string">&quot;f2b9441a&quot;</span> .</span><br><span class="line">              <span class="comment">// Nonce</span></span><br><span class="line">              <span class="string">&quot;42a14695&quot;</span>;</span><br><span class="line"><span class="variable">$header_bin</span> = <span class="title function_ invoke__">pack</span>(<span class="string">&quot;H*&quot;</span>, <span class="variable">$header_hex</span>);  <span class="comment">// hex to bin</span></span><br><span class="line"><span class="variable">$h</span> = <span class="title function_ invoke__">hash</span>(<span class="string">&#x27;sha256&#x27;</span>, <span class="title function_ invoke__">hash</span>(<span class="string">&#x27;sha256&#x27;</span>, <span class="variable">$header_bin</span>, <span class="literal">true</span>), <span class="literal">true</span>);  <span class="comment">// double sha256</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">echo</span> <span class="title function_ invoke__">bin2hex</span>(<span class="variable">$h</span>), <span class="string">&quot;\n&quot;</span>;</span><br><span class="line"><span class="comment">// output: 1dbd981fe6985776b644b173a4d0385ddc1aa2a829688d1e0000000000000000</span></span><br><span class="line"><span class="keyword">echo</span> <span class="title function_ invoke__">bin2hex</span>(<span class="title function_ invoke__">strrev</span>(<span class="variable">$h</span>)), <span class="string">&quot;\n&quot;</span>;</span><br><span class="line"><span class="comment">// output: 00000000000000001e8d6829a8a21adc5d38d0a473b144b6765798e61f98bd1d</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><p>该计算过程简单明了：首先将数个字段合并成一块数据，然后对这块数据进行双SHA256运算。</p><h4 id="产量调节"><a href="#产量调节" class="headerlink" title="产量调节"></a>产量调节</h4><p>Block的产量为大约每两周2016个，即每10分钟一块。该规则在每个节点的代码里都固定了。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 目标时间窗口长度：两周</span></span><br><span class="line"><span class="type">static</span> <span class="type">const</span> int64 nTargetTimespan = <span class="number">14</span> * <span class="number">24</span> * <span class="number">60</span> * <span class="number">60</span>;</span><br><span class="line"><span class="comment">// block频率，每10分钟一块</span></span><br><span class="line"><span class="type">static</span> <span class="type">const</span> int64 nTargetSpacing  = <span class="number">10</span> * <span class="number">60</span>;</span><br><span class="line"><span class="comment">// 每两周的产量2016，也是调节周期</span></span><br><span class="line"><span class="type">static</span> <span class="type">const</span> int64 nInterval       = nTargetTimespan / nTargetSpacing;</span><br></pre></td></tr></table></figure><p>但由于实际算力总是不断变化的（目前一直是快速上升的），所以需根据最近2016个块的耗费时间来调整难度值，维持每10分钟一个block的频率.</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Only change once per interval</span></span><br><span class="line"><span class="keyword">if</span> ((pindexLast-&gt;nHeight<span class="number">+1</span>) % nInterval != <span class="number">0</span>) &#123;</span><br><span class="line">    <span class="comment">// 未达到周期个数，无需调节</span></span><br><span class="line">    <span class="keyword">return</span> pindexLast-&gt;nBits;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Go back by what we want to be 14 days worth of blocks</span></span><br><span class="line"><span class="type">const</span> CBlockIndex* pindexFirst = pindexLast;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; pindexFirst &amp;&amp; i &lt; nInterval<span class="number">-1</span>; i++)</span><br><span class="line">    pindexFirst = pindexFirst-&gt;pprev;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 计算本次2016个块的实际产生时间</span></span><br><span class="line"><span class="comment">// Limit adjustment step</span></span><br><span class="line">int64 nActualTimespan = pindexLast-&gt;<span class="built_in">GetBlockTime</span>() - pindexFirst-&gt;<span class="built_in">GetBlockTime</span>();</span><br><span class="line"><span class="comment">// 限定幅度，最低为1/4，最高为4倍</span></span><br><span class="line"><span class="keyword">if</span> (nActualTimespan &lt; nTargetTimespan/<span class="number">4</span>)</span><br><span class="line">    nActualTimespan = nTargetTimespan/<span class="number">4</span>;</span><br><span class="line"><span class="keyword">if</span> (nActualTimespan &gt; nTargetTimespan*<span class="number">4</span>)</span><br><span class="line">    nActualTimespan = nTargetTimespan*<span class="number">4</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 根据最近2016个块的时间，重新计算目标难度 </span></span><br><span class="line"><span class="comment">// Retarget</span></span><br><span class="line">CBigNum bnNew;</span><br><span class="line">bnNew.<span class="built_in">SetCompact</span>(pindexLast-&gt;nBits);</span><br><span class="line">bnNew *= nActualTimespan;</span><br><span class="line">bnNew /= nTargetTimespan;</span><br><span class="line"> </span><br><span class="line"><span class="keyword">if</span> (bnNew &gt; bnProofOfWorkLimit)</span><br><span class="line">    bnNew = bnProofOfWorkLimit;</span><br><span class="line"> </span><br><span class="line"><span class="keyword">return</span> bnNew.<span class="built_in">GetCompact</span>();</span><br></pre></td></tr></table></figure><h4 id="Block字段详解"><a href="#Block字段详解" class="headerlink" title="Block字段详解"></a>Block字段详解</h4><ul><li>Version，版本号，很少变动，一般用于软件全网升级时做标识</li><li>hashPrevBlock，前向Block Hash值，该字段强制多个Block之间形成链接</li><li>hashMerkleRoot，交易Hash树的根节点Hash值，起校验作用，保障Block在网络传输过程中的数据一致性，有新交易加入即发生变化</li><li>Time，Unix时间戳，每秒自增一，标记Block的生成时间，同时为block hash探寻引入一个频繁的变动因子</li><li>Bits，可以推算出难度值，用于验证block hash难度是否达标</li><li>Nonce，随机数，在上面数个字段都固定的情况下，不停地更换随机数来探寻</li></ul><p>最为关键的字段是<code>hashPrevBlock</code>，该字段使得Block之间链接起来，形成一个巨大的“链条”。Block本是稀松平常的数据结构，但以链式结构组织起来后却使得它们具有非常深远的意义：</p><ol><li>形成分支博弈，使得算力总是在主分支上角逐</li><li>算力攻击的概率难度呈指数上升（泊松分布）</li></ol><p>每个block都必须指向前一个block，否则无法验证通过。追溯至源头，便是高度为零的创世纪块(Genesis Block)，这里是Block Chain的起点，其前向block hash为零，或者说为空。</p><h4 id="新block诞生过程"><a href="#新block诞生过程" class="headerlink" title="新block诞生过程"></a>新block诞生过程</h4><p>下面是一个简单的步骤描述，实际矿池运作会有区别，复杂一些：</p><ol><li>节点监听全网交易，通过验证的交易进入节点的内存池(Tx Mem Pool)，并更新交易数据的Merkle Hash值</li><li>更新时间戳</li><li>尝试不同的随机数(Nonce)，进行hash计算</li><li>重复该过程至找到合理的hash</li><li>打包block：先装入block meta信息，然后是交易数据</li><li>对外部广播出新block</li><li>其他节点验证通过后，链接至Block Chain，主链高度加一，然后切换至新block后面挖矿</li></ol><p>由于hashPrevBlock字段的存在，使得大家总是在最新的block后面开挖，稍后会分析原因。</p><h4 id="主链分叉"><a href="#主链分叉" class="headerlink" title="主链分叉"></a>主链分叉</h4><p>从block hash算法我们知道，合理的block并不是唯一的，同一高度存在多个block的可能性。那么，当同一个高度出现多个时，主链即出现分叉(Fork)。遇到分叉时，网络会根据下列原则选举出Best Chain：</p><ol><li>不同高度的分支，总是接受最高（即最长）的那条分支</li><li>相同高度的，接受难度最大的</li><li>高度相同且难度一致的，接受时间最早的</li><li>若所有均相同，则按照从网络接受的顺序</li><li>等待Block Chain高度增一，则重新选择Best Chain</li></ol><p><img src="https://f.cloud.github.com/assets/514951/864443/94b6ba76-f621-11e2-95b0-febc373535b7.png" alt="blockchain"></p><p>按照这个规则运作的节点，称为诚实节点(Honest Nodes)。节点可以诚实也可以不诚实。</p><h4 id="分支博弈"><a href="#分支博弈" class="headerlink" title="分支博弈"></a>分支博弈</h4><p>我们假设所有的节点：</p><ol><li>都是理性的，追求收益最大化</li><li>都是不诚实的，且不惜任何手段获取利益</li></ol><p>所有节点均独自挖矿不理会其他节点，并将所得收益放入自己口袋，现象就是一个节点挖一个分支。由于机器的配置总是有差别的，那么算力最强的节点挖得的分支必然是最长的，如果一个节点的分支不是最长的，意味其收益存在不被认可的风险（即零收益）。为了降低、逃避此风险，一些节点肯定会联合起来一起挖某个分支，试图成为最长的分支或保持最长分支优势。</p><p>一旦出现有少量的节点联合，那么其他节点必然会效仿，否则他们收益为零的风险会更大。于是，分支迅速合并汇集，所有节点都会选择算力更强的分支，只有这样才能保持收益风险最小。最终，只会存在一个这样的分支，就是主干分支(Best&#x2F;Main Chain)。</p><p>对于不诚实节点来说，结局是无奈的：能且只能加入主干挖矿。不加入即意味被抛弃，零收益；加入就是老实干活，按占比分成。</p><h4 id="Hash-Dance"><a href="#Hash-Dance" class="headerlink" title="Hash Dance"></a>Hash Dance</h4><p>Block hash的计算是随机概率事件，当有节点广播出难度更高的block后，大家便跑到那个分支。在比特币系统运行过程中，算力经常在分支间跳来跳去，此现象称为<code>Hash Dance</code>。一般情况下，分支的高度为1~2，没有大的故障很难出现高于2的分支。</p><p>Hash Dance起名源于<a href="https://www.google.com.hk/search?q=google+dance">Google Dance</a>.</p><h4 id="算力攻击的概率"><a href="#算力攻击的概率" class="headerlink" title="算力攻击的概率"></a>算力攻击的概率</h4><p><em>本节内容参考：<a href="https://bitcoin.org/bitcoin.pdf">Bitcoin: A Peer-to-Peer Electronic Cash System</a></em></p><p>算力攻击是一个概率问题，这里作简单叙述：</p><ul><li>p &#x3D; 诚实节点挖出block概率</li><li>q &#x3D; 攻击者挖出block概率，q &#x3D; 1 - p</li><li>qz &#x3D; 攻击者从z个block追上的概率</li></ul><p><img src="https://f.cloud.github.com/assets/514951/902244/a22dae50-fb92-11e2-95aa-3e346efdab40.png" alt="算力攻击的概率"></p><p>我们假设p&gt;q，否则攻击者掌握了一半以上的算力，那么概率上永远是赢的。该事件（攻击者胜出）的概率是固定，且N次事件之间是相互独立的，那么这一系列随机过程符合<code>泊松分布(Poisson Distribution)</code>。<em>Z</em>个块时，攻击者胜出的期望为<em>lambda</em>：</p><p><img src="https://f.cloud.github.com/assets/514951/905393/e386fa6c-fc20-11e2-8376-73d9a367bf98.png" alt="攻击者胜出的期望"></p><p>攻击者在攻击时已经偷偷的计算了<em>k</em>个块，那么这<em>k</em>个块概率符合泊松分布(下图左侧部分)，若<em>k&lt;&#x3D;z</em>，那么追赶上后续<em>z-k</em>个块的概率为*(q&#x2F;p)^(z-k)*，即：</p><p><img src="https://f.cloud.github.com/assets/514951/905394/d08dfcca-fc21-11e2-9740-907f2a89de88.png" alt="k个块概率符合泊松分布"></p><p>展开为如下形式：</p><p><img src="https://f.cloud.github.com/assets/514951/905406/b08c63ec-fc23-11e2-8f43-c932daec9f7c.png" alt="k个块概率符合泊松分布"></p><p>计算该过程的C语言代码如下：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;math.h&gt;</span></span><span class="type">double</span> <span class="title function_">AttackerSuccessProbability</span><span class="params">(<span class="type">double</span> q, <span class="type">int</span> z)</span>&#123;    <span class="type">double</span> sum    = <span class="number">1.0</span>;    <span class="type">double</span> p      = <span class="number">1.0</span> - q;    <span class="type">double</span> lambda = z * (q / p);    <span class="type">int</span> i, k;    <span class="keyword">for</span> (k = <span class="number">0</span>; k &lt;= z; k++) &#123;        <span class="type">double</span> poisson = <span class="built_in">exp</span>(-lambda);        <span class="keyword">for</span> (i = <span class="number">1</span>; i &lt;= k; i++)            poisson *= lambda / i;        sum -= poisson * (<span class="number">1</span> - <span class="built_in">pow</span>(q / p, z - k));    &#125;    <span class="keyword">return</span> sum;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>我们选取几个值，结果如下：</p><p><img src="https://f.cloud.github.com/assets/514951/905408/e6c5ed84-fc23-11e2-9334-ca8e768c5aa0.png" alt="概率结果"></p><p>可以看到，由于block的链式形式，随着块数的上升，攻击者赢得的概率呈指数下降。这是很多应用等待六个甚至六个以上确认的原因，一旦超过N个确认，攻击者得逞的可能微乎其微，概率值快速趋近零。</p><p>当攻击者的算力超过50%时，便可以控制Block Chain，俗称51%攻击。</p><h4 id="算力攻击的危害"><a href="#算力攻击的危害" class="headerlink" title="算力攻击的危害"></a>算力攻击的危害</h4><p>攻击者算出block后，block&amp;Txs必须能够通过验证，否则其他节点都会拒掉，攻击便无意义。攻击者<strong>无法</strong>做出下列行为：</p><ol><li>偷盗他人的币。消费某个地址的币时，需要对应的ECDSA私钥签名，而私钥是无法破解的。</li><li>凭空制造比特币。每个block奖励的币值是统一的规则，篡改奖励币值会导致其他节点会拒绝该block。</li></ol><p>唯一的益处是可以选择性的收录进入block的交易，对自己的币进行<code>多重消费(Double Spending)</code>。</p><p>过程是这样的：假设现在block高度为100，攻击者给商户发了一个交易10BTC，记作交易A，通常这笔交易会被收录进高度101的block中，当商户在101块中看到这笔交易后，就把货物给了攻击者。此时，攻击者便开始构造另一个高度为101的block，但用交易B替换了交易A，交易B中的输入是同一笔，使得发给商户的那笔钱发给他自己。同时，攻击者需要努力计算block，使得他的分支能够赶上主分支，并合并(Merge)被大家接受，一旦接受，便成功地完成了一次Double Spending。</p><p>攻击难度呈指数上升，所以成功的Double Spending通常是一个极小概率事件。</p><h4 id="算力巨头"><a href="#算力巨头" class="headerlink" title="算力巨头"></a>算力巨头</h4><p>全网算力的上升对比特币是极其有利的，这是毫无疑问的。但目前大矿池与矿业巨头使得算力高度集中化，这与中本聪所设想的<code>一CPU一票（one-CPU-one-vote）</code>的分散局面背道而驰，或许是他未曾预料的。</p><p>挖矿是一项专业劳动，最后必然会交给最专业的人或团队，因为这样才能实现资源配置最优，效率最高。普通投资人通过购买算力巨头的股票：1. 完成投资；2. 分享算力红利。看似中心化的背后其实依然是分散的：</p><ol><li>矿业公司的背后是无数分散的投资人</li><li>矿池背后是无数分散的个体算力</li></ol><p>既得利益使得算力巨头倾向于维护系统而不是破坏，因其收益均建立在比特币系统之上，既得利益者断然不会搬石头砸自己脚。甚至很多巨头在达到一定算力占比后会主动控制算力增长，使得低于某阈值内。</p><h3 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h3><p>本篇几乎都在讲挖矿，因为挖矿对于比特币系统来说实在是太重要了。需要了解：1. block是基于工作量证明的。2. block以链式结构存在时的深远意义。</p>]]>
    </content>
    <id>https://kevinpan.page/2013/08/03/proof-of-work-and-mining/</id>
    <link href="https://kevinpan.page/2013/08/03/proof-of-work-and-mining/"/>
    <published>2013-08-04T01:01:00.000Z</published>
    <summary>
      <![CDATA[<h3 id="工作证明"><a href="#工作证明" class="headerlink" title="工作证明"></a>工作证明</h3><p>工作证明(Proof Of Work，简称POW)，顾名思义，即工作量的证明。通常来说只能从结果证明，因为监测工作过程通常是]]>
    </summary>
    <title>工作证明与挖矿</title>
    <updated>2013-08-04T01:01:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Kevin Pan</name>
      <email>bit.kevin@gmail.com</email>
    </author>
    <content>
      <![CDATA[<h3 id="基本概念"><a href="#基本概念" class="headerlink" title="基本概念"></a>基本概念</h3><h4 id="椭圆曲线数字签名算法"><a href="#椭圆曲线数字签名算法" class="headerlink" title="椭圆曲线数字签名算法"></a>椭圆曲线数字签名算法</h4><p>椭圆曲线数字签名算法（ECDSA）是使用椭圆曲线对数字签名算法（DSA）的模拟，该算法是构成比特币系统的基石。</p><h5 id="私钥"><a href="#私钥" class="headerlink" title="私钥"></a>私钥</h5><p>非公开，拥有者需安全保管。通常是由随机算法生成的，说白了，就是一个巨大的随机整数，256位、32字节。大小介于<code>1 ~ 0xFFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFE BAAE DCE6 AF48 A03B BFD2 5E8C D036 4141</code>之间的数，都可以认为是一个合法的私钥。于是，除了随机方法外，采用特定算法由固定的输入，得到32字节输出的算法就可以成为得到私钥的方法。于是，便有了迷你私钥(Mini Privkey)，原理很简单，例如，采用SHA256的一种实现：</p><pre><code>private key = SHA256(&lt;passphase&gt;)</code></pre><p>迷你私钥存在安全问题，因为输入集合太小，易被构造常见组合的彩虹表暴力破解，所以通常还是使用系统随机生成的比较好，无安全隐患。</p><h5 id="公钥"><a href="#公钥" class="headerlink" title="公钥"></a>公钥</h5><p>公钥与私钥是相对应的，一把私钥可以推出唯一的公钥，但公钥却无法推导出私钥。公钥有两种形式：压缩与非压缩。</p><p>早期比特币均使用非压缩公钥，现大部分客户端已默认使用压缩公钥。这个貌似是比特币系统一个长得像feature的bug，早期人少活多代码写得不够精细，openssl库的文档又不足够好，导致Satoshi以为必须使用非压缩的完整公钥，后来大家发现其实公钥的左右两个32字节是有关联的，左侧(X)可以推出右侧(Y)的平方值，有左侧(X)就可以了。</p><p>现在系统里两种方式共存，应该会一直共存下去。两种公钥的首个字节为标识位，压缩为33字节，非压缩为65字节。以0x04开头为非压缩，0x02&#x2F;0x03开头为压缩公钥，0x02&#x2F;0x03的选取由右侧Y开方后的奇偶决定。</p><p>压缩形式可以减小Tx&#x2F;Block的体积，每个Tx Input减少32字节。</p><h5 id="签名"><a href="#签名" class="headerlink" title="签名"></a>签名</h5><p>使用私钥对数据进行签署(Sign)会得到签名(Signature)。通常会将数据先生成Hash值，然后对此Hash值进行签名。签名(signature)有两部分组成: R + S。由签名(signature)与Hash值，便可以推出一个公钥，验证此公钥，便可知道此签名是否由公钥对应的私钥签名。</p><p>通常，每个签名会有三个长度：73、72、71，符合校验的概率为25%、50%、25%。所以每次签署后，需要找出符合校验的签名长度，再提供给验证方。</p><h5 id="地址"><a href="#地址" class="headerlink" title="地址"></a>地址</h5><p>地址是为了人们交换方便而弄出来的一个方案，因为公钥太长了(130字符串或66字符串)。地址长度为25字节，转为base58编码后，为34或35个字符。base58是类似base64的编码，但去掉了易引起视觉混淆的字符，又在地址末尾添加了4个字节校验位，保障在人们交换个别字符错误时，也能够因地址校验失败而制止了误操作。</p><p>由于存在公钥有两种形式，那么一个公钥便对应两个地址。这两个地址都可由同一私钥签署交易。</p><p>公钥生成地址的算法：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Version = 1 byte of 0 (zero); on the test network, this is 1 byte of 111</span><br><span class="line">Key hash = Version concatenated with RIPEMD-160(SHA-256(public key))</span><br><span class="line">Checksum = 1st 4 bytes of SHA-256(SHA-256(Key hash))</span><br><span class="line">Bitcoin Address = Base58Encode(Key hash concatenated with Checksum)</span><br></pre></td></tr></table></figure><p>下图是非压缩公钥生成地址的过程：</p><p><img src="https://f.cloud.github.com/assets/514951/880381/6290a5ea-f93a-11e2-9ac4-835e86a38642.png" alt="pubkeytoaddr"></p><p>对于压缩公钥生成地址时，则只取公钥的X部分即可。</p><h5 id="推导关系"><a href="#推导关系" class="headerlink" title="推导关系"></a>推导关系</h5><p>三者推导关系：私钥 &gt;&gt; 公钥 &gt;&gt; 两个地址。过程均不可逆。拥有私钥便拥有一切，但通常为了方便，会把对应的公钥、地址也存储起来。</p><h4 id="交易"><a href="#交易" class="headerlink" title="交易"></a>交易</h4><p>比特币的交易(Transation，缩写Tx)，并不是通常意义的交易，例如一手交钱一手交货，而是转账。交易由N个输入和M个输出两部分组成。交易的每个输入便是前向交易的某个输出，那么追踪到源头，必然出现一个没有输入的交易，此类交易称为CoinBase Tx。CoinBase类交易是奖励挖矿者而产生的交易，该交易总是位于Block块的第一笔。</p><p><img src="https://f.cloud.github.com/assets/514951/864163/7c9c862e-f61b-11e2-9dfe-324d025e5dd3.png" alt="qq20130727-18"></p><p>拥有一个输入与输出的Tx数据：</p><pre><code>Input:Previous tx: f5d8ee39a430901c91a5917b9f2dc19d6d1a0e9cea205b009ca73dd04470b9a6Index: 0scriptSig: 304502206e21798a42fae0e854281abd38bacd1aeed3ee3738d9e1446618c4571d1090db022100e2ac980643b0b82c0e88ffdfec6b64e3e6ba35e7ba5fdd7d5d6cc8d25c6b241501Output:Value: 5000000000scriptPubKey: OP_DUP OP_HASH160 404371705fa9bd789a2fcd52d2c580b65d35549dOP_EQUALVERIFY OP_CHECKSIG</code></pre><p>一旦某个Tx的第N个输出成为另一个Tx的输入，那么该笔比特币即为已花费。每个交易有唯一Hash字符串来标识，通过对交易数据做两次SHA256哈希运算而来：</p><pre><code>Tx Hash ID = SHA256(SHA256(Tx Data))</code></pre><h4 id="矿工费"><a href="#矿工费" class="headerlink" title="矿工费"></a>矿工费</h4><p>矿工费（Transaction Fee）是鼓励矿工将Tx打包进Block的激励报酬。计算一笔交易的矿工费：</p><pre><code>Transaction Fee = SUM(Input&#39;s amount) - SUM(Output&#39;s amount)</code></pre><p>每笔Tx的矿工费必然大于等于零，否则该笔Tx即为非法，不会被网络接收。</p><h4 id="数据块"><a href="#数据块" class="headerlink" title="数据块"></a>数据块</h4><p>数据块(Block)是存储Block Meta与Tx的地方。Block的第一笔Tx总是CoinBase Tx，因此Block中的交易数量总是大于等于1，随后是这段时间内网络广播出来的Tx。</p><p>找到合适的Block是一件非常困难的事情，需要通过大量的数学计算才能发现，该计算过程称为“挖矿”。首个发现者，会得到一些比特币作为奖励。</p><h4 id="数据链"><a href="#数据链" class="headerlink" title="数据链"></a>数据链</h4><p>多个Block连接起来成为数据链(Block Chain)。</p><p><img src="https://f.cloud.github.com/assets/514951/864443/94b6ba76-f621-11e2-95b0-febc373535b7.png" alt="blockchain"></p><p>为了引入容错与竞争机制，比特币系统允许Block Chain出现分叉，但每个节点总是倾向于选择最高的、难度最大的链，并称之为Best Chain，节点只认可Best Chain上的数据。</p><p>首个Block称为Genesis Block，并设定高度为零，后续每新增一个Block，高度则递增一。目前是不允许花费Genesis Block中的比特币的。</p><ul><li>每个Block中的Tx在此Block中均唯一</li><li>一个Tx通常只会在一个Block里，也可能会出现在多个Block中，但只会在Best Chain中的某一个Block出现一次</li></ul><h4 id="货币存储"><a href="#货币存储" class="headerlink" title="货币存储"></a>货币存储</h4><p>比特币是密码货币、纯数字化货币，没有看得见摸得着的硬币或纸币。一个人持有比特币意味着：</p><ol><li>其拥有一些地址的私钥</li><li>这些地址是数笔交易的输出，且未花费</li></ol><p>所有货币记录均以交易形式存储在整个blockchain数据块中，<code>无交易无货币</code>。货币不会凭空产生，也不会凭空消失。遗失了某个地址的私钥，意味着该地址上的Tx无法签署，无法成为下一个Tx的输入，便认为该笔比特币永久消失了。</p><h4 id="货币发行"><a href="#货币发行" class="headerlink" title="货币发行"></a>货币发行</h4><p>既然所有交易的输入源头都是来自CoinBase，产生CoinBase时即意味着货币发行。比特币采用衰减发行，每四年产量减半，第一个四年每个block的coinbase奖励50BTC，随后是25btc, 12.5btc, …并最终于2140年为零，此时总量达到极限为2100万个btc。</p><p><img src="https://f.cloud.github.com/assets/514951/864426/153fcb3e-f621-11e2-8322-0b2f2f425ef7.png" alt="total_bitcoins_over_time_graph"></p><p>减半周期，严格来说，并不是准确的四年，而是每生成210000个block。之所以俗称四年减半，是因为比特币系统会根据全网算力的大小自动调整难度系统，使得大约每两周产生2016个block，那么四年约21万块block。</p><p>该函数<code>GetBlockValue()</code>用于计算挖得Block的奖励值：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">int64 <span class="type">static</span> <span class="title">GetBlockValue</span><span class="params">(<span class="type">int</span> nHeight, int64 nFees)</span></span></span><br><span class="line"><span class="function"></span>&#123;                            </span><br><span class="line">    int64 nSubsidy = <span class="number">50</span> * COIN;     </span><br><span class="line">                             </span><br><span class="line">    <span class="comment">// Subsidy is cut in half every 210000 blocks, which will occur approximately every 4 years</span></span><br><span class="line">    nSubsidy &gt;&gt;= (nHeight / <span class="number">210000</span>);</span><br><span class="line">                             </span><br><span class="line">    <span class="keyword">return</span> nSubsidy + nFees; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>当达到2100万btc以后，不再有来自CoinBase的奖励了，矿工的收入来源仅剩下交易的矿工费。此时，每个block的收入绝对值btc很低，但此时比特币应当会非常繁荣，币值也会相当的高，使得矿工们依然有利可图。</p><h4 id="杜绝多重支付"><a href="#杜绝多重支付" class="headerlink" title="杜绝多重支付"></a>杜绝多重支付</h4><p>传统货币存在多重支付(Double Spending)问题，典型的比如非数字时代的支票诈骗、数字时代的信用卡诈骗等。在比特币系统里，每笔交易的确认均需要得到全网广播，并收录进Block后才能得到真正确认。每笔钱的花销，均需要检测上次输入交易的状态。数据是带时间戳的、公开的，BlockChain由巨大的算力保障其安全性。所以比特币系统将货币的多重支付的风险极大降低，几近于零。通过等待多个Block确认，更是从概率上降低至零。一般得到6个确认后，可认为非常安全。但对于能影响你人生的重大支付，建议等待20~30个确认。</p><h4 id="匿名性"><a href="#匿名性" class="headerlink" title="匿名性"></a>匿名性</h4><p>任何人均可以轻易生成大量的私钥、公钥、地址。地址本身是匿名的，通过多个地址交易可进一步提高匿名性。但该匿名性并不像媒体宣传的那样，是某种程度上的匿名。因为比特币的交易数据是公开的，所以任何一笔资金的流向均是可以追踪的。</p><p>不了解比特币的人为它的匿名性产生一些担忧，比如担心更利于从事非法业务；了解比特币的人却因为它的伪匿名性而苦恼。传统货币在消费中也是匿名的，且是法律保障的，大部分国家都不允许个人涂画纸币。</p><p>地址本身是匿名的，但你可以通过地址对应的私钥签名消息来向公众证明你拥有某个比特币地址。</p><h3 id="其他名词"><a href="#其他名词" class="headerlink" title="其他名词"></a>其他名词</h3><h4 id="哈希"><a href="#哈希" class="headerlink" title="哈希"></a>哈希</h4><p>哈希(Hash)是一种函数，将一个数映射到另一个集合当中。不同的哈希函数映射的空间不同，反映到计算机上就是生成的值长度不一样。同一个哈希函数，相同的输入必然是相同的输出，但同一个输出却可能有不同的输入，这种情况称为哈希碰撞。</p><p>常见的哈希函数有<code>CRC32</code>, <code>MD5</code>, <code>SHA1</code>, <code>SHA-256</code>, <code>SHA-512</code>, <code>RIPEMD-160</code>等，哈希函数在计算中有着非常广泛的用途。比特币里主要采用的是<code>SHA-256</code>和<code>RIPEMD-160</code>。</p><h4 id="脑钱包-纸钱包"><a href="#脑钱包-纸钱包" class="headerlink" title="脑钱包&amp;纸钱包"></a>脑钱包&amp;纸钱包</h4><p>前面提到过的脑钱包与纸钱包，这其实不算是钱包的分类，只是生成、存储密钥的方式而已。脑钱包属于迷你私钥的产物。脑钱包就是记在脑袋里的密钥，纸钱包就是打印到纸上的密钥，仅此而已。</p><p>有同学提到过，以一个计算机文件作为输入，例如一个数MB大小的照片，通过某种Hash运算后得到私钥的方法。这个方案的安全性还是不错的，同时可以防止盗私钥木马根据特征扫描私钥。文本形式存储私钥是有特征的，而一个照片文件却难以察觉，即使放在云盘等第三方存储空间中都是安全的。</p>]]>
    </content>
    <id>https://kevinpan.page/2013/07/30/bitcoin-basic/</id>
    <link href="https://kevinpan.page/2013/07/30/bitcoin-basic/"/>
    <published>2013-07-31T04:58:00.000Z</published>
    <summary>
      <![CDATA[<h3 id="基本概念"><a href="#基本概念" class="headerlink" title="基本概念"></a>基本概念</h3><h4 id="椭圆曲线数字签名算法"><a href="#椭圆曲线数字签名算法" class="headerlink" titl]]>
    </summary>
    <title>比特币基础知识</title>
    <updated>2013-07-31T04:58:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>Kevin Pan</name>
      <email>bit.kevin@gmail.com</email>
    </author>
    <content>
      <![CDATA[<h3 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h3><p>拥有和保存比特币，需要通过客户端，通常把该软件称为钱包。目前，整个比特币项目由<code>Bitcoin Foundation</code>来开发与维护，通常把他们称为官方团队。官方推出的客户端是<code>Bitcoin Qt</code>, 由C++编写核心功能，GUI界面由Python Qt完成。不含有GUI界面的被成为<code>bitcoind</code>，许多服务与核心功能均由其实现。运行bitcoind的通常称为节点(Bitcoin Node), 一个节点通常拥有完整的BlockChain数据，并实时与外界网络同步更新。</p><p>钱包中通常含有：</p><ul><li>公钥、私钥、地址</li><li>与钱包中地址相关的交易信息</li><li>其他辅助数据</li></ul><p>最核心的数据就是密钥，拥有密钥便拥有一切，相关信息均可由其而来。钱包并不一定需要包含完整的BlockChain数据，不包含BlockChain数据的钱包称为轻钱包(Light Weight Wallet)。对于大部分日常使用来讲，轻钱包便足够了。</p><h3 id="分类"><a href="#分类" class="headerlink" title="分类"></a>分类</h3><ul><li>完全节点型(Full Node)：含有BlockChain所有完整数据</li><li>简易节点型(SPV Node)：Header-Only Clients，仅有Block头部信息，无需交易数据</li><li>CS型(Server-Client)：服务端-客户端模式，大部分数据存储在服务端</li><li>BS型：所有数据均通过浏览器在线使用</li></ul><p>Bitcoin Qt，因为其是一款完整的钱包软件，需要下载大约超过<code>10GB</code>的BlockChain数据(24万个block)，对于大部分人来讲，是没有必要的。目前，官方主页上默认推荐的客户端已经不再是Bitcoin Qt, 而是<a href="https://multibit.org/">MultiBit</a>（支持Windows, MacOS和Linux的轻钱包）；移动端目前最好用的是<a href="https://play.google.com/store/apps/details?id=de.schildbach.wallet">Bitcoin Wallet</a>(安卓平台)，iOS平台由于政策原因，一直未有出色的软件，Blockchain.Info为iOS提供了一个简单的钱包软件，<a href="http://itunes.com/apps/blockchain">Blockchain for the iPhone</a>。还有就是在线钱包，如优秀的<a href="https://blockchain.info/">BlockChain.Info</a>，其安全性均超过自行保存管理，推荐使用之。</p><p>最近还有一种流行的钱包：脑钱包。因其安全性较低，并不推荐大家使用，仅临时性场合使用之。其原理是由一串密码短语，通过Hash运算，得到密钥，只要记住这串密语即可使用钱包。因为密码短语符合大家习惯和记忆特点，可以通过计算大量常见组合来破解。除了暴力破解的问题外，失忆是最大的风险，比如摔个跟头跌成脑震荡，或长期不用自然忘得一干二净。</p><h3 id="选择-存储"><a href="#选择-存储" class="headerlink" title="选择&amp;存储"></a>选择&amp;存储</h3><ul><li><p>日常使用的额度通常小于10个币，可以存放在电脑或手机App中。通常存放1个币以下是比较保险的，丢了不太心疼嘛</p></li><li><p>持有几十、几百个币的，可以选择BlockChain.Info，Inputs.io等在线钱包。其也可以当做日常钱包使用。</p></li><li><p>持有上千甚至数万的，应该分开存储，并隔离存放。使用离线电脑生产密钥，打印出来托管至银行等高安全场所存储，并销毁现有密钥。同时还需要多份隔离存储，甚至对密钥进行加密。</p></li></ul><p>密钥即一切，如不慎弄丢钱包，便永远失去这笔比特币。所以钱包需要小心妥善保管，不在自己的PC或者手机App中存储大量比特币，丢失的风险太高，病毒木马、硬件损坏、手机丢失等均造成无法挽救的损失。俗话讲鸡蛋不要搁在一个篮子里，多种方式存储也是降低风险的有效方式。目前丢失的比特币或有数百万BTC之巨。</p><h3 id="常见钱包"><a href="#常见钱包" class="headerlink" title="常见钱包"></a>常见钱包</h3><ul><li><a href="https://en.bitcoin.it/wiki/Bitcoin-Qt">Bitcoin-Qt</a> - 官方客户端，基于C++&#x2F;Qt，全平台，完全数据。</li><li><a href="https://en.bitcoin.it/wiki/MultiBit">MultiBit</a> - 全平台，轻钱包，官方推荐</li><li><a href="https://en.bitcoin.it/wiki/Electrum">Electrum</a> - 著名轻钱包</li><li><a href="https://en.bitcoin.it/wiki/Armory">Armory</a> - 基于Python，含有诸多特性的轻钱包</li><li><a href="https://blockchain.info/">BlockChain.info</a> - 非常著名在线钱包</li></ul><h3 id="开发库"><a href="#开发库" class="headerlink" title="开发库"></a>开发库</h3><ul><li><a href="https://en.bitcoin.it/wiki/Bitcoind">bitcoind</a> - 官方客户端，无GUI，开发者必备</li><li><a href="https://github.com/libcoin/libcoin">libcoin</a> - libcoin</li><li><a href="https://github.com/spesmilo/libbitcoin">libbitcoin</a> - asynchronous C++ library for Bitcoin</li><li><a href="https://github.com/MatthewLM/cbitcoin">cbitcoin</a> - A low-level bitcoin library written in standard C</li><li><a href="https://code.google.com/p/bitcoinj/">Bitcoinj</a> - a Java implementation of the Bitcoin protocol</li><li><a href="https://github.com/piotrnar/gocoin">gocoin</a> - Bitcoin client library for Go &#x2F; golang</li><li><a href="https://github.com/jgarzik/pynode">pynode</a> - Bitcoin P2P router, in python</li><li><a href="https://github.com/gavinandresen/bitcointools">bitcointools</a> - Python-based tools for the Bitcoin cryptocurrency system，By Gavin Andresen</li><li><a href="https://github.com/jtobey/bitcoin-abe">bitcoin-abe</a> - Abe: block browser for Bitcoin and similar currencies</li></ul><h3 id="数据检索"><a href="#数据检索" class="headerlink" title="数据检索"></a>数据检索</h3><ul><li><a href="https://blockchain.info/">BlockChain.info</a></li><li><a href="http://blockexplorer.com/">Bitcoin Block Explorer</a></li></ul><hr><h4 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h4><ol><li>Bitcoin Foundation： <a href="https://bitcoinfoundation.org/">https://bitcoinfoundation.org/</a></li><li>Why Apple Is Afraid Of Bitcoin: <a href="http://www.forbes.com/sites/jonmatonis/2012/06/13/why-apple-is-afraid-of-bitcoin/">http://www.forbes.com/sites/jonmatonis/2012/06/13/why-apple-is-afraid-of-bitcoin/</a></li><li>List of Bitcoin-related software： <a href="https://en.bitcoin.it/wiki/Software">https://en.bitcoin.it/wiki/Software</a></li><li>Bitcoin Clients: <a href="https://en.bitcoin.it/wiki/Clients">https://en.bitcoin.it/wiki/Clients</a></li></ol>]]>
    </content>
    <id>https://kevinpan.page/2013/07/20/bitcoin-clients/</id>
    <link href="https://kevinpan.page/2013/07/20/bitcoin-clients/"/>
    <published>2013-07-20T22:00:00.000Z</published>
    <summary>
      <![CDATA[<h3 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h3><p>拥有和保存比特币，需要通过客户端，通常把该软件称为钱包。目前，整个比特币项目由<code>Bitcoin Foundation</code>]]>
    </summary>
    <title>比特币客户端与钱包</title>
    <updated>2013-07-20T22:00:00.000Z</updated>
  </entry>
</feed>
