<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://blog.dex.moe/feed.xml" rel="self" type="application/atom+xml" /><link href="https://blog.dex.moe/" rel="alternate" type="text/html" /><updated>2026-10-13T21:45:55+00:00</updated><id>https://blog.dex.moe/feed.xml</id><title type="html">Dex’s blog</title><subtitle>Musings of a computer scientist.</subtitle><entry><title type="html">sft1200科学上网设置</title><link href="https://blog.dex.moe/router/tutorial/2024/01/07/sft1200-wifi-science-setting.html" rel="alternate" type="text/html" title="sft1200科学上网设置" /><published>2024-01-07T21:14:43+00:00</published><updated>2024-01-07T21:14:43+00:00</updated><id>https://blog.dex.moe/router/tutorial/2024/01/07/sft1200-wifi-science-setting</id><content type="html" xml:base="https://blog.dex.moe/router/tutorial/2024/01/07/sft1200-wifi-science-setting.html"><![CDATA[<p>马上要到农历新年了，所以有差旅+科学上网（工作）的需求，这两天看了不少方案，包括travel router（比如GLiNet的mt3000等）和最新5g cpe，然后就黄鱼淘了一支sft1200（主要是便宜，才100rmb），迅速设置了一下就可以愉快的使用了。</p>

<h2 id="sft1200安装shellclash性能不高就这个比较合适">sft1200安装shellclash（性能不高就这个比较合适）</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>export url='https://gh.jwsc.eu.org/master' &amp;&amp; sh -c "$(curl -kfsSl $url/install.sh)" &amp;&amp; source /etc/profile &amp;&gt; /dev/null
</code></pre></div></div>

<p>设置好以后登录 <a href="http://192.168.8.1:9999/ui/#/proxies">http://192.168.8.1:9999/ui/#/proxies</a> 就可以访问了，注意不是31</p>

<h2 id="其他">其他</h2>
<p>还有些操作来源这个<a href="https://www.cnblogs.com/Edwardlyz/articles/16712323.html">折腾指南</a></p>

<ol>
  <li>关闭2.4Gwifi <em>试了但感觉提高不明显</em></li>
  <li>超频
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> echo 1000000 &gt; /sys/devices/system/cpu/cpu0/cpufreq/scaling_setspeed
</code></pre></div>    </div>
    <p><em>试了有所提高</em></p>
  </li>
  <li>提高5G wifi 功耗
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> vim /etc/config/wireless
</code></pre></div>    </div>
    <p>在5G部分 country改为AU，txpower_max设置为31，之后去192.168.8.1的设置界面，将信道设置为149，功率设置为最高（31dbm） <em>试了有所提高</em></p>
  </li>
</ol>

<h3 id="操作前">操作前</h3>
<p><img src="/assets/images/sft1200-1.png" alt="" /></p>

<h3 id="操作后">操作后</h3>
<p><img src="/assets/images/sft1200-2.png" alt="" /></p>

<h2 id="总结">总结</h2>
<p>之后还是会关注5g cpe相关的，4g和5g的速率差距还是挺明显的。（不过5g需要锁n78频段）</p>]]></content><author><name></name></author><category term="router" /><category term="tutorial" /><category term="wifi" /><category term="router" /><category term="tutorial" /><summary type="html"><![CDATA[马上要到农历新年了，所以有差旅+科学上网（工作）的需求，这两天看了不少方案，包括travel router（比如GLiNet的mt3000等）和最新5g cpe，然后就黄鱼淘了一支sft1200（主要是便宜，才100rmb），迅速设置了一下就可以愉快的使用了。]]></summary></entry><entry><title type="html">How to Write Custom Allreduce Operator in Horovod</title><link href="https://blog.dex.moe/tutorial/2021/06/08/how-to-write-custom-allreduce-operation.html" rel="alternate" type="text/html" title="How to Write Custom Allreduce Operator in Horovod" /><published>2021-06-08T21:11:02+00:00</published><updated>2021-06-08T21:11:02+00:00</updated><id>https://blog.dex.moe/tutorial/2021/06/08/how-to-write-custom-allreduce-operation</id><content type="html" xml:base="https://blog.dex.moe/tutorial/2021/06/08/how-to-write-custom-allreduce-operation.html"><![CDATA[<h2 id="prequisites"><a href="#prep">Prequisites</a></h2>

<ul>
  <li>Version: Python &gt;= 3.6</li>
  <li>Install CMake, e.g.
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo apt-get update 
$ sudo apt-get install build-essential libssl-dev
$ sudo apt-get install cmake
</code></pre></div>    </div>
  </li>
  <li>Download horovod source code
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ git clone https://github.com/horovod/horovod.git
</code></pre></div>    </div>
  </li>
  <li>Install third party modules
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ cd horovod
$ git submodule update--init --recursive
</code></pre></div>    </div>
  </li>
  <li><em>Recommended</em>: Use a virtual environment
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ virtualenv hvdenv
$ . hvdenv/bin/activate
</code></pre></div>    </div>
  </li>
  <li>Install Tensorflow, Pytorch and/or MXNet</li>
  <li>Download/Install NCCL2/MPI</li>
</ul>

<h2 id="intro"><a href="#Intro">Intro</a></h2>

<p>As documented in <a href="https://horovod.readthedocs.io/en/stable/contributors_include.html#adding-custom-operations">Horovod Contributor Guide</a>, the base allreduce operation is <code class="language-plaintext highlighter-rouge">AllreduceOp</code> which is definted at <a href="https://github.com/horovod/horovod/blob/master/horovod/common/ops/collective_operations.h#L51"><code class="language-plaintext highlighter-rouge">collective_operations.cc</code></a></p>

<h2 id="methodology"><a href="#Methodology">Methodology</a></h2>

<p>For example, we want to write our custom hierarchical allreduce operator (<code class="language-plaintext highlighter-rouge">NCCL_REDUCE</code>+<code class="language-plaintext highlighter-rouge">NCCL_ALLREDUCE</code>+<code class="language-plaintext highlighter-rouge">NCCL_BCAST</code>) based on the existing one (<code class="language-plaintext highlighter-rouge">NCCL_REDUCESCATTER</code>+<code class="language-plaintext highlighter-rouge">MPI_ALLREDUCE</code>+<code class="language-plaintext highlighter-rouge">NCCL_ALLGATHER</code>).</p>

<h3 id="1-hot-fix"><a href="#1-hot-fix">1 Hot-fix</a></h3>

<ol>
  <li>Navigate to <code class="language-plaintext highlighter-rouge">nccl_operations.cc</code></li>
  <li>Find <code class="language-plaintext highlighter-rouge">NCCLAllreduce::Execute</code></li>
  <li>Replace Code with CustomNCCLHierarchicalAllreduceOp</li>
</ol>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#if HAVE_MPI
</span><span class="n">Status</span>
<span class="n">NCCLHierarchicalAllreduce</span><span class="o">::</span><span class="n">Execute</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">TensorTableEntry</span><span class="o">&gt;&amp;</span> <span class="n">entries</span><span class="p">,</span>
                                   <span class="k">const</span> <span class="n">Response</span><span class="o">&amp;</span> <span class="n">response</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">auto</span><span class="o">&amp;</span> <span class="n">first_entry</span> <span class="o">=</span> <span class="n">entries</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>

  <span class="c1">// Determine GPU IDs of the devices participating in this communicator.</span>
  <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int32_t</span><span class="o">&gt;</span> <span class="n">nccl_device_map</span><span class="p">;</span>
  <span class="n">nccl_device_map</span><span class="p">.</span><span class="n">reserve</span><span class="p">(</span>
      <span class="n">global_state_</span><span class="o">-&gt;</span><span class="n">controller</span><span class="o">-&gt;</span><span class="n">GetLocalCommRanks</span><span class="p">().</span><span class="n">size</span><span class="p">());</span>
  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">rank</span> <span class="o">:</span> <span class="n">global_state_</span><span class="o">-&gt;</span><span class="n">controller</span><span class="o">-&gt;</span><span class="n">GetLocalCommRanks</span><span class="p">())</span> <span class="p">{</span>
    <span class="n">nccl_device_map</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">response</span><span class="p">.</span><span class="n">devices</span><span class="p">()[</span><span class="n">rank</span><span class="p">]);</span>
  <span class="p">}</span>

  <span class="n">gpu_op_context_</span><span class="p">.</span><span class="n">InitGPU</span><span class="p">(</span><span class="n">entries</span><span class="p">);</span>
  <span class="n">nccl_op_context_</span><span class="p">.</span><span class="n">InitNCCLComm</span><span class="p">(</span><span class="n">entries</span><span class="p">,</span> <span class="n">nccl_device_map</span><span class="p">);</span>
  <span class="n">gpu_op_context_</span><span class="p">.</span><span class="n">InitGPUQueue</span><span class="p">(</span><span class="n">entries</span><span class="p">,</span> <span class="n">response</span><span class="p">);</span>

  <span class="k">const</span> <span class="kt">void</span><span class="o">*</span> <span class="n">fused_input_data</span><span class="p">;</span>
  <span class="kt">void</span><span class="o">*</span> <span class="n">buffer_data</span><span class="p">;</span>
  <span class="kt">size_t</span> <span class="n">buffer_len</span><span class="p">;</span>

  <span class="c1">// Copy memory into the fusion buffer.</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">entries</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">MemcpyInFusionBuffer</span><span class="p">(</span><span class="n">entries</span><span class="p">,</span> <span class="n">fused_input_data</span><span class="p">,</span> <span class="n">buffer_data</span><span class="p">,</span> <span class="n">buffer_len</span><span class="p">);</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">global_state_</span><span class="o">-&gt;</span><span class="n">timeline</span><span class="p">.</span><span class="n">Initialized</span><span class="p">())</span> <span class="p">{</span>
      <span class="n">gpu_context_</span><span class="o">-&gt;</span><span class="n">RecordEvent</span><span class="p">(</span><span class="n">gpu_op_context_</span><span class="p">.</span><span class="n">event_queue</span><span class="p">,</span> <span class="n">MEMCPY_IN_FUSION_BUFFER</span><span class="p">,</span> <span class="o">*</span><span class="n">gpu_op_context_</span><span class="p">.</span><span class="n">stream</span><span class="p">);</span>
    <span class="p">}</span>
  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="n">fused_input_data</span> <span class="o">=</span> <span class="n">first_entry</span><span class="p">.</span><span class="n">tensor</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">();</span>
    <span class="n">buffer_data</span> <span class="o">=</span> <span class="p">(</span><span class="kt">void</span><span class="o">*</span><span class="p">)</span> <span class="n">first_entry</span><span class="p">.</span><span class="n">output</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">();</span>
    <span class="n">buffer_len</span> <span class="o">=</span> <span class="p">(</span><span class="kt">size_t</span><span class="p">)</span> <span class="n">first_entry</span><span class="p">.</span><span class="n">output</span><span class="o">-&gt;</span><span class="n">size</span><span class="p">();</span>
  <span class="p">}</span>

  <span class="kt">int64_t</span> <span class="n">num_elements</span> <span class="o">=</span> <span class="n">buffer_len</span> <span class="o">/</span> <span class="n">DataType_Size</span><span class="p">(</span><span class="n">first_entry</span><span class="p">.</span><span class="n">tensor</span><span class="o">-&gt;</span><span class="n">dtype</span><span class="p">());</span>

  <span class="k">if</span> <span class="p">(</span><span class="n">response</span><span class="p">.</span><span class="n">prescale_factor</span><span class="p">()</span> <span class="o">!=</span> <span class="mf">1.0</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// Execute prescaling op</span>
    <span class="n">ScaleBuffer</span><span class="p">(</span><span class="n">response</span><span class="p">.</span><span class="n">prescale_factor</span><span class="p">(),</span> <span class="n">entries</span><span class="p">,</span> <span class="n">fused_input_data</span><span class="p">,</span> <span class="n">buffer_data</span><span class="p">,</span> <span class="n">num_elements</span><span class="p">);</span>
    <span class="n">fused_input_data</span> <span class="o">=</span> <span class="n">buffer_data</span><span class="p">;</span> <span class="c1">// for unfused, scale is done out of place</span>
  <span class="p">}</span>

  <span class="c1">// Do allreduce.</span>
  <span class="kt">int</span> <span class="n">element_size</span> <span class="o">=</span> <span class="n">mpi_context_</span><span class="o">-&gt;</span><span class="n">GetMPITypeSize</span><span class="p">(</span><span class="n">first_entry</span><span class="p">.</span><span class="n">tensor</span><span class="o">-&gt;</span><span class="n">dtype</span><span class="p">());</span>
  <span class="kt">int</span> <span class="n">local_size</span> <span class="o">=</span> <span class="n">global_state_</span><span class="o">-&gt;</span><span class="n">controller</span><span class="o">-&gt;</span><span class="n">GetLocalSize</span><span class="p">();</span>
  <span class="kt">int</span> <span class="n">local_rank</span> <span class="o">=</span> <span class="n">global_state_</span><span class="o">-&gt;</span><span class="n">controller</span><span class="o">-&gt;</span><span class="n">GetLocalRank</span><span class="p">();</span>

  <span class="c1">// If cluster is homogeneous and we are using fusion buffer, include</span>
  <span class="c1">// dummy elements from the buffer (if necessary) to make sure the data</span>
  <span class="c1">// is divisible by local_size. This is always possible since we</span>
  <span class="c1">// set the fusion buffer size divisible by local_size.</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">global_state_</span><span class="o">-&gt;</span><span class="n">controller</span><span class="o">-&gt;</span><span class="n">IsHomogeneous</span><span class="p">()</span> <span class="o">&amp;&amp;</span> <span class="n">entries</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// Making sure the number of elements is divisible by</span>
    <span class="c1">// FUSION_BUFFER_ATOMIC_UNIT for improved performance</span>
    <span class="kt">int</span> <span class="n">div</span> <span class="o">=</span> <span class="n">local_size</span> <span class="o">*</span> <span class="n">FUSION_BUFFER_ATOMIC_UNIT</span><span class="p">;</span>
    <span class="n">num_elements</span> <span class="o">=</span> <span class="p">((</span><span class="n">num_elements</span> <span class="o">+</span> <span class="n">div</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">/</span> <span class="n">div</span><span class="p">)</span> <span class="o">*</span> <span class="n">div</span><span class="p">;</span>
    <span class="n">buffer_len</span> <span class="o">=</span> <span class="n">num_elements</span> <span class="o">*</span> <span class="n">element_size</span><span class="p">;</span>
  <span class="p">}</span>

 

  <span class="kt">int</span> <span class="n">root_rank</span> <span class="o">=</span>
      <span class="n">global_state_</span><span class="o">-&gt;</span><span class="n">controller</span><span class="o">-&gt;</span><span class="n">IsHomogeneous</span><span class="p">()</span> <span class="o">?</span> <span class="n">local_size</span> <span class="o">-</span> <span class="mi">1</span> <span class="o">:</span> <span class="mi">0</span><span class="p">;</span>

  <span class="k">auto</span><span class="o">&amp;</span> <span class="n">timeline</span> <span class="o">=</span> <span class="n">global_state_</span><span class="o">-&gt;</span><span class="n">timeline</span><span class="p">;</span>



  <span class="k">auto</span> <span class="n">nccl_result</span> <span class="o">=</span> <span class="n">ncclReduce</span><span class="p">(</span><span class="n">fused_input_data</span><span class="p">,</span>
                                <span class="n">buffer_data</span><span class="p">,</span>
                                <span class="p">(</span><span class="kt">size_t</span><span class="p">)</span> <span class="n">num_elements</span><span class="p">,</span>
                                <span class="n">GetNCCLDataType</span><span class="p">(</span><span class="n">first_entry</span><span class="p">.</span><span class="n">tensor</span><span class="p">),</span> <span class="n">ncclSum</span><span class="p">,</span>
                                <span class="n">root_rank</span><span class="p">,</span> <span class="o">*</span><span class="n">nccl_op_context_</span><span class="p">.</span><span class="n">nccl_comm_</span><span class="p">,</span> <span class="o">*</span><span class="n">gpu_op_context_</span><span class="p">.</span><span class="n">stream</span><span class="p">);</span>
  <span class="n">nccl_context_</span><span class="o">-&gt;</span><span class="n">ErrorCheck</span><span class="p">(</span><span class="s">"ncclReduce"</span><span class="p">,</span> <span class="n">nccl_result</span><span class="p">,</span> <span class="o">*</span><span class="n">nccl_op_context_</span><span class="p">.</span><span class="n">nccl_comm_</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">global_state_</span><span class="o">-&gt;</span><span class="n">timeline</span><span class="p">.</span><span class="n">Initialized</span><span class="p">())</span> <span class="p">{</span>
    <span class="n">gpu_context_</span><span class="o">-&gt;</span><span class="n">RecordEvent</span><span class="p">(</span><span class="n">gpu_op_context_</span><span class="p">.</span><span class="n">event_queue</span><span class="p">,</span> <span class="n">NCCL_REDUCE</span><span class="p">,</span> <span class="o">*</span><span class="n">gpu_op_context_</span><span class="p">.</span><span class="n">stream</span><span class="p">);</span>
  <span class="p">}</span>

  <span class="n">nccl_context_</span><span class="o">-&gt;</span><span class="n">ErrorCheck</span><span class="p">(</span><span class="s">"ncclAllReduce"</span><span class="p">,</span> 
                            <span class="n">ncclAllReduce</span><span class="p">(</span><span class="n">fused_input_data</span><span class="p">,</span> <span class="n">buffer_data</span><span class="p">,</span>
                                   <span class="p">(</span><span class="kt">size_t</span><span class="p">)</span> <span class="n">num_elements</span><span class="p">,</span>
                                   <span class="n">GetNCCLDataType</span><span class="p">(</span><span class="n">first_entry</span><span class="p">.</span><span class="n">tensor</span><span class="p">),</span> <span class="n">ncclSum</span><span class="p">,</span>
                                   <span class="o">*</span><span class="n">nccl_op_context_</span><span class="p">.</span><span class="n">nccl_comm_</span><span class="p">,</span> <span class="o">*</span><span class="n">gpu_op_context_</span><span class="p">.</span><span class="n">stream</span><span class="p">),</span>
                            <span class="o">*</span><span class="n">nccl_op_context_</span><span class="p">.</span><span class="n">nccl_comm_</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">global_state_</span><span class="o">-&gt;</span><span class="n">timeline</span><span class="p">.</span><span class="n">Initialized</span><span class="p">())</span> <span class="p">{</span>
    <span class="n">gpu_context_</span><span class="o">-&gt;</span><span class="n">RecordEvent</span><span class="p">(</span><span class="n">gpu_op_context_</span><span class="p">.</span><span class="n">event_queue</span><span class="p">,</span> <span class="n">NCCL_ALLREDUCE</span><span class="p">,</span> <span class="o">*</span><span class="n">gpu_op_context_</span><span class="p">.</span><span class="n">stream</span><span class="p">);</span>
  <span class="p">}</span>


  <span class="n">nccl_context_</span><span class="o">-&gt;</span><span class="n">ErrorCheck</span><span class="p">(</span><span class="s">"ncclBcast"</span><span class="p">,</span>
                            <span class="n">ncclBcast</span><span class="p">(</span><span class="n">buffer_data</span><span class="p">,</span>
                                      <span class="p">(</span><span class="kt">size_t</span><span class="p">)</span> <span class="n">num_elements</span><span class="p">,</span>
                                      <span class="n">GetNCCLDataType</span><span class="p">(</span><span class="n">first_entry</span><span class="p">.</span><span class="n">tensor</span><span class="p">),</span> <span class="n">root_rank</span><span class="p">,</span>
                                      <span class="o">*</span><span class="n">nccl_op_context_</span><span class="p">.</span><span class="n">nccl_comm_</span><span class="p">,</span> <span class="o">*</span><span class="n">gpu_op_context_</span><span class="p">.</span><span class="n">stream</span><span class="p">),</span>
                            <span class="o">*</span><span class="n">nccl_op_context_</span><span class="p">.</span><span class="n">nccl_comm_</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">global_state_</span><span class="o">-&gt;</span><span class="n">timeline</span><span class="p">.</span><span class="n">Initialized</span><span class="p">())</span> <span class="p">{</span>
    <span class="n">gpu_context_</span><span class="o">-&gt;</span><span class="n">RecordEvent</span><span class="p">(</span><span class="n">gpu_op_context_</span><span class="p">.</span><span class="n">event_queue</span><span class="p">,</span> <span class="n">NCCL_BCAST</span><span class="p">,</span> <span class="o">*</span><span class="n">gpu_op_context_</span><span class="p">.</span><span class="n">stream</span><span class="p">);</span>
  <span class="p">}</span>
  

  <span class="k">if</span> <span class="p">(</span><span class="n">response</span><span class="p">.</span><span class="n">postscale_factor</span><span class="p">()</span> <span class="o">!=</span> <span class="mf">1.0</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// Execute postscaling op</span>
    <span class="n">ScaleBuffer</span><span class="p">(</span><span class="n">response</span><span class="p">.</span><span class="n">postscale_factor</span><span class="p">(),</span> <span class="n">entries</span><span class="p">,</span> <span class="n">buffer_data</span><span class="p">,</span> <span class="n">buffer_data</span><span class="p">,</span> <span class="n">num_elements</span><span class="p">);</span>
  <span class="p">}</span>

  <span class="c1">// Copy memory out of the fusion buffer.</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">entries</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">MemcpyOutFusionBuffer</span><span class="p">(</span><span class="n">buffer_data</span><span class="p">,</span> <span class="n">entries</span><span class="p">);</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">global_state_</span><span class="o">-&gt;</span><span class="n">timeline</span><span class="p">.</span><span class="n">Initialized</span><span class="p">())</span> <span class="p">{</span>
      <span class="n">gpu_context_</span><span class="o">-&gt;</span><span class="n">RecordEvent</span><span class="p">(</span><span class="n">gpu_op_context_</span><span class="p">.</span><span class="n">event_queue</span><span class="p">,</span> <span class="n">MEMCPY_OUT_FUSION_BUFFER</span><span class="p">,</span> <span class="o">*</span><span class="n">gpu_op_context_</span><span class="p">.</span><span class="n">stream</span><span class="p">);</span>
    <span class="p">}</span>
  <span class="p">}</span>

  <span class="k">return</span> <span class="n">gpu_op_context_</span><span class="p">.</span><span class="n">FinalizeGPUQueue</span><span class="p">(</span><span class="n">entries</span><span class="p">,</span> <span class="nb">true</span><span class="p">,</span> <span class="n">nccl_op_context_</span><span class="p">.</span><span class="n">error_check_callback_</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="2-define-customop-with-a-different-name"><a href="#2-define-customop">2 Define CustomOp with A Different Name</a></h3>

<ol>
  <li>Navigate to <code class="language-plaintext highlighter-rouge">nccl_operations.h</code></li>
  <li>Inherit from <code class="language-plaintext highlighter-rouge">NCCLAllreduce</code> and define a custom operator such as <code class="language-plaintext highlighter-rouge">CustomNCCLHierarchicalAllreduce</code> and define the public function name as same</li>
</ol>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#if HAVE_MPI
</span><span class="k">class</span> <span class="nc">CustomNCCLHierarchicalAllreduce</span> <span class="o">:</span> <span class="k">public</span> <span class="n">NCCLAllreduce</span> <span class="p">{</span>
<span class="nl">public:</span>
  <span class="n">CustomNCCLHierarchicalAllreduce</span><span class="p">(</span><span class="n">NCCLContext</span><span class="o">*</span> <span class="n">nccl_context</span><span class="p">,</span> <span class="n">MPIContext</span><span class="o">*</span> <span class="n">mpi_context</span><span class="p">,</span>
                            <span class="n">GPUContext</span><span class="o">*</span> <span class="n">gpu_context</span><span class="p">,</span>
                            <span class="n">HorovodGlobalState</span><span class="o">*</span> <span class="n">global_state</span><span class="p">)</span>
      <span class="o">:</span> <span class="n">NCCLAllreduce</span><span class="p">(</span><span class="n">nccl_context</span><span class="p">,</span> <span class="n">gpu_context</span><span class="p">,</span> <span class="n">global_state</span><span class="p">,</span> <span class="n">Communicator</span><span class="o">::</span><span class="n">LOCAL</span><span class="p">),</span>
        <span class="n">mpi_context_</span><span class="p">(</span><span class="n">mpi_context</span><span class="p">){};</span>

  <span class="n">Status</span> <span class="n">Execute</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">TensorTableEntry</span><span class="o">&gt;&amp;</span> <span class="n">entries</span><span class="p">,</span>
                 <span class="k">const</span> <span class="n">Response</span><span class="o">&amp;</span> <span class="n">response</span><span class="p">)</span> <span class="k">override</span><span class="p">;</span>

  <span class="kt">bool</span> <span class="n">Enabled</span><span class="p">(</span><span class="k">const</span> <span class="n">ParameterManager</span><span class="o">&amp;</span> <span class="n">param_manager</span><span class="p">,</span>
               <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">TensorTableEntry</span><span class="o">&gt;&amp;</span> <span class="n">entries</span><span class="p">,</span>
               <span class="k">const</span> <span class="n">Response</span><span class="o">&amp;</span> <span class="n">response</span><span class="p">)</span> <span class="k">const</span> <span class="k">override</span><span class="p">;</span>

<span class="nl">private:</span>
  <span class="n">MPIContext</span><span class="o">*</span> <span class="n">mpi_context_</span><span class="p">;</span>
<span class="p">};</span>
<span class="cp">#endif
</span></code></pre></div></div>

<ol>
  <li>Write the implementation of the custom defined operation in <code class="language-plaintext highlighter-rouge">nccl_operations.cc</code> with both <code class="language-plaintext highlighter-rouge">Execute</code> and <code class="language-plaintext highlighter-rouge">Enabled</code> member function</li>
</ol>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#if HAVE_MPI
</span><span class="n">Status</span>
<span class="n">CustomNCCLHierarchicalAllreduce</span><span class="o">::</span><span class="n">Execute</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">TensorTableEntry</span><span class="o">&gt;&amp;</span> <span class="n">entries</span><span class="p">,</span>
                                   <span class="k">const</span> <span class="n">Response</span><span class="o">&amp;</span> <span class="n">response</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">auto</span><span class="o">&amp;</span> <span class="n">first_entry</span> <span class="o">=</span> <span class="n">entries</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>

  <span class="c1">// Determine GPU IDs of the devices participating in this communicator.</span>
  <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int32_t</span><span class="o">&gt;</span> <span class="n">nccl_device_map</span><span class="p">;</span>
  <span class="n">nccl_device_map</span><span class="p">.</span><span class="n">reserve</span><span class="p">(</span>
      <span class="n">global_state_</span><span class="o">-&gt;</span><span class="n">controller</span><span class="o">-&gt;</span><span class="n">GetLocalCommRanks</span><span class="p">().</span><span class="n">size</span><span class="p">());</span>
  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">rank</span> <span class="o">:</span> <span class="n">global_state_</span><span class="o">-&gt;</span><span class="n">controller</span><span class="o">-&gt;</span><span class="n">GetLocalCommRanks</span><span class="p">())</span> <span class="p">{</span>
    <span class="n">nccl_device_map</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">response</span><span class="p">.</span><span class="n">devices</span><span class="p">()[</span><span class="n">rank</span><span class="p">]);</span>
  <span class="p">}</span>

  <span class="n">gpu_op_context_</span><span class="p">.</span><span class="n">InitGPU</span><span class="p">(</span><span class="n">entries</span><span class="p">);</span>
  <span class="n">nccl_op_context_</span><span class="p">.</span><span class="n">InitNCCLComm</span><span class="p">(</span><span class="n">entries</span><span class="p">,</span> <span class="n">nccl_device_map</span><span class="p">);</span>
  <span class="n">gpu_op_context_</span><span class="p">.</span><span class="n">InitGPUQueue</span><span class="p">(</span><span class="n">entries</span><span class="p">,</span> <span class="n">response</span><span class="p">);</span>

  <span class="k">const</span> <span class="kt">void</span><span class="o">*</span> <span class="n">fused_input_data</span><span class="p">;</span>
  <span class="kt">void</span><span class="o">*</span> <span class="n">buffer_data</span><span class="p">;</span>
  <span class="kt">size_t</span> <span class="n">buffer_len</span><span class="p">;</span>

  <span class="c1">// Copy memory into the fusion buffer.</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">entries</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">MemcpyInFusionBuffer</span><span class="p">(</span><span class="n">entries</span><span class="p">,</span> <span class="n">fused_input_data</span><span class="p">,</span> <span class="n">buffer_data</span><span class="p">,</span> <span class="n">buffer_len</span><span class="p">);</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">global_state_</span><span class="o">-&gt;</span><span class="n">timeline</span><span class="p">.</span><span class="n">Initialized</span><span class="p">())</span> <span class="p">{</span>
      <span class="n">gpu_context_</span><span class="o">-&gt;</span><span class="n">RecordEvent</span><span class="p">(</span><span class="n">gpu_op_context_</span><span class="p">.</span><span class="n">event_queue</span><span class="p">,</span> <span class="n">MEMCPY_IN_FUSION_BUFFER</span><span class="p">,</span> <span class="o">*</span><span class="n">gpu_op_context_</span><span class="p">.</span><span class="n">stream</span><span class="p">);</span>
    <span class="p">}</span>
  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="n">fused_input_data</span> <span class="o">=</span> <span class="n">first_entry</span><span class="p">.</span><span class="n">tensor</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">();</span>
    <span class="n">buffer_data</span> <span class="o">=</span> <span class="p">(</span><span class="kt">void</span><span class="o">*</span><span class="p">)</span> <span class="n">first_entry</span><span class="p">.</span><span class="n">output</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">();</span>
    <span class="n">buffer_len</span> <span class="o">=</span> <span class="p">(</span><span class="kt">size_t</span><span class="p">)</span> <span class="n">first_entry</span><span class="p">.</span><span class="n">output</span><span class="o">-&gt;</span><span class="n">size</span><span class="p">();</span>
  <span class="p">}</span>

  <span class="kt">int64_t</span> <span class="n">num_elements</span> <span class="o">=</span> <span class="n">buffer_len</span> <span class="o">/</span> <span class="n">DataType_Size</span><span class="p">(</span><span class="n">first_entry</span><span class="p">.</span><span class="n">tensor</span><span class="o">-&gt;</span><span class="n">dtype</span><span class="p">());</span>

  <span class="k">if</span> <span class="p">(</span><span class="n">response</span><span class="p">.</span><span class="n">prescale_factor</span><span class="p">()</span> <span class="o">!=</span> <span class="mf">1.0</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// Execute prescaling op</span>
    <span class="n">ScaleBuffer</span><span class="p">(</span><span class="n">response</span><span class="p">.</span><span class="n">prescale_factor</span><span class="p">(),</span> <span class="n">entries</span><span class="p">,</span> <span class="n">fused_input_data</span><span class="p">,</span> <span class="n">buffer_data</span><span class="p">,</span> <span class="n">num_elements</span><span class="p">);</span>
    <span class="n">fused_input_data</span> <span class="o">=</span> <span class="n">buffer_data</span><span class="p">;</span> <span class="c1">// for unfused, scale is done out of place</span>
  <span class="p">}</span>

  <span class="c1">// Do allreduce.</span>
  <span class="kt">int</span> <span class="n">element_size</span> <span class="o">=</span> <span class="n">mpi_context_</span><span class="o">-&gt;</span><span class="n">GetMPITypeSize</span><span class="p">(</span><span class="n">first_entry</span><span class="p">.</span><span class="n">tensor</span><span class="o">-&gt;</span><span class="n">dtype</span><span class="p">());</span>
  <span class="kt">int</span> <span class="n">local_size</span> <span class="o">=</span> <span class="n">global_state_</span><span class="o">-&gt;</span><span class="n">controller</span><span class="o">-&gt;</span><span class="n">GetLocalSize</span><span class="p">();</span>
  <span class="kt">int</span> <span class="n">local_rank</span> <span class="o">=</span> <span class="n">global_state_</span><span class="o">-&gt;</span><span class="n">controller</span><span class="o">-&gt;</span><span class="n">GetLocalRank</span><span class="p">();</span>

  <span class="c1">// If cluster is homogeneous and we are using fusion buffer, include</span>
  <span class="c1">// dummy elements from the buffer (if necessary) to make sure the data</span>
  <span class="c1">// is divisible by local_size. This is always possible since we</span>
  <span class="c1">// set the fusion buffer size divisible by local_size.</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">global_state_</span><span class="o">-&gt;</span><span class="n">controller</span><span class="o">-&gt;</span><span class="n">IsHomogeneous</span><span class="p">()</span> <span class="o">&amp;&amp;</span> <span class="n">entries</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// Making sure the number of elements is divisible by</span>
    <span class="c1">// FUSION_BUFFER_ATOMIC_UNIT for improved performance</span>
    <span class="kt">int</span> <span class="n">div</span> <span class="o">=</span> <span class="n">local_size</span> <span class="o">*</span> <span class="n">FUSION_BUFFER_ATOMIC_UNIT</span><span class="p">;</span>
    <span class="n">num_elements</span> <span class="o">=</span> <span class="p">((</span><span class="n">num_elements</span> <span class="o">+</span> <span class="n">div</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">/</span> <span class="n">div</span><span class="p">)</span> <span class="o">*</span> <span class="n">div</span><span class="p">;</span>
    <span class="n">buffer_len</span> <span class="o">=</span> <span class="n">num_elements</span> <span class="o">*</span> <span class="n">element_size</span><span class="p">;</span>
  <span class="p">}</span>

 

  <span class="kt">int</span> <span class="n">root_rank</span> <span class="o">=</span>
      <span class="n">global_state_</span><span class="o">-&gt;</span><span class="n">controller</span><span class="o">-&gt;</span><span class="n">IsHomogeneous</span><span class="p">()</span> <span class="o">?</span> <span class="n">local_size</span> <span class="o">-</span> <span class="mi">1</span> <span class="o">:</span> <span class="mi">0</span><span class="p">;</span>

  <span class="k">auto</span><span class="o">&amp;</span> <span class="n">timeline</span> <span class="o">=</span> <span class="n">global_state_</span><span class="o">-&gt;</span><span class="n">timeline</span><span class="p">;</span>



  <span class="k">auto</span> <span class="n">nccl_result</span> <span class="o">=</span> <span class="n">ncclReduce</span><span class="p">(</span><span class="n">fused_input_data</span><span class="p">,</span>
                                <span class="n">buffer_data</span><span class="p">,</span>
                                <span class="p">(</span><span class="kt">size_t</span><span class="p">)</span> <span class="n">num_elements</span><span class="p">,</span>
                                <span class="n">GetNCCLDataType</span><span class="p">(</span><span class="n">first_entry</span><span class="p">.</span><span class="n">tensor</span><span class="p">),</span> <span class="n">ncclSum</span><span class="p">,</span>
                                <span class="n">root_rank</span><span class="p">,</span> <span class="o">*</span><span class="n">nccl_op_context_</span><span class="p">.</span><span class="n">nccl_comm_</span><span class="p">,</span> <span class="o">*</span><span class="n">gpu_op_context_</span><span class="p">.</span><span class="n">stream</span><span class="p">);</span>
  <span class="n">nccl_context_</span><span class="o">-&gt;</span><span class="n">ErrorCheck</span><span class="p">(</span><span class="s">"ncclReduce"</span><span class="p">,</span> <span class="n">nccl_result</span><span class="p">,</span> <span class="o">*</span><span class="n">nccl_op_context_</span><span class="p">.</span><span class="n">nccl_comm_</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">global_state_</span><span class="o">-&gt;</span><span class="n">timeline</span><span class="p">.</span><span class="n">Initialized</span><span class="p">())</span> <span class="p">{</span>
    <span class="n">gpu_context_</span><span class="o">-&gt;</span><span class="n">RecordEvent</span><span class="p">(</span><span class="n">gpu_op_context_</span><span class="p">.</span><span class="n">event_queue</span><span class="p">,</span> <span class="n">NCCL_REDUCE</span><span class="p">,</span> <span class="o">*</span><span class="n">gpu_op_context_</span><span class="p">.</span><span class="n">stream</span><span class="p">);</span>
  <span class="p">}</span>

  <span class="n">nccl_context_</span><span class="o">-&gt;</span><span class="n">ErrorCheck</span><span class="p">(</span><span class="s">"ncclAllReduce"</span><span class="p">,</span> 
                            <span class="n">ncclAllReduce</span><span class="p">(</span><span class="n">fused_input_data</span><span class="p">,</span> <span class="n">buffer_data</span><span class="p">,</span>
                                   <span class="p">(</span><span class="kt">size_t</span><span class="p">)</span> <span class="n">num_elements</span><span class="p">,</span>
                                   <span class="n">GetNCCLDataType</span><span class="p">(</span><span class="n">first_entry</span><span class="p">.</span><span class="n">tensor</span><span class="p">),</span> <span class="n">ncclSum</span><span class="p">,</span>
                                   <span class="o">*</span><span class="n">nccl_op_context_</span><span class="p">.</span><span class="n">nccl_comm_</span><span class="p">,</span> <span class="o">*</span><span class="n">gpu_op_context_</span><span class="p">.</span><span class="n">stream</span><span class="p">),</span>
                            <span class="o">*</span><span class="n">nccl_op_context_</span><span class="p">.</span><span class="n">nccl_comm_</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">global_state_</span><span class="o">-&gt;</span><span class="n">timeline</span><span class="p">.</span><span class="n">Initialized</span><span class="p">())</span> <span class="p">{</span>
    <span class="n">gpu_context_</span><span class="o">-&gt;</span><span class="n">RecordEvent</span><span class="p">(</span><span class="n">gpu_op_context_</span><span class="p">.</span><span class="n">event_queue</span><span class="p">,</span> <span class="n">NCCL_ALLREDUCE</span><span class="p">,</span> <span class="o">*</span><span class="n">gpu_op_context_</span><span class="p">.</span><span class="n">stream</span><span class="p">);</span>
  <span class="p">}</span>


  <span class="n">nccl_context_</span><span class="o">-&gt;</span><span class="n">ErrorCheck</span><span class="p">(</span><span class="s">"ncclBcast"</span><span class="p">,</span>
                            <span class="n">ncclBcast</span><span class="p">(</span><span class="n">buffer_data</span><span class="p">,</span>
                                      <span class="p">(</span><span class="kt">size_t</span><span class="p">)</span> <span class="n">num_elements</span><span class="p">,</span>
                                      <span class="n">GetNCCLDataType</span><span class="p">(</span><span class="n">first_entry</span><span class="p">.</span><span class="n">tensor</span><span class="p">),</span> <span class="n">root_rank</span><span class="p">,</span>
                                      <span class="o">*</span><span class="n">nccl_op_context_</span><span class="p">.</span><span class="n">nccl_comm_</span><span class="p">,</span> <span class="o">*</span><span class="n">gpu_op_context_</span><span class="p">.</span><span class="n">stream</span><span class="p">),</span>
                            <span class="o">*</span><span class="n">nccl_op_context_</span><span class="p">.</span><span class="n">nccl_comm_</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">global_state_</span><span class="o">-&gt;</span><span class="n">timeline</span><span class="p">.</span><span class="n">Initialized</span><span class="p">())</span> <span class="p">{</span>
    <span class="n">gpu_context_</span><span class="o">-&gt;</span><span class="n">RecordEvent</span><span class="p">(</span><span class="n">gpu_op_context_</span><span class="p">.</span><span class="n">event_queue</span><span class="p">,</span> <span class="n">NCCL_BCAST</span><span class="p">,</span> <span class="o">*</span><span class="n">gpu_op_context_</span><span class="p">.</span><span class="n">stream</span><span class="p">);</span>
  <span class="p">}</span>
  

  <span class="k">if</span> <span class="p">(</span><span class="n">response</span><span class="p">.</span><span class="n">postscale_factor</span><span class="p">()</span> <span class="o">!=</span> <span class="mf">1.0</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// Execute postscaling op</span>
    <span class="n">ScaleBuffer</span><span class="p">(</span><span class="n">response</span><span class="p">.</span><span class="n">postscale_factor</span><span class="p">(),</span> <span class="n">entries</span><span class="p">,</span> <span class="n">buffer_data</span><span class="p">,</span> <span class="n">buffer_data</span><span class="p">,</span> <span class="n">num_elements</span><span class="p">);</span>
  <span class="p">}</span>

  <span class="c1">// Copy memory out of the fusion buffer.</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">entries</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">MemcpyOutFusionBuffer</span><span class="p">(</span><span class="n">buffer_data</span><span class="p">,</span> <span class="n">entries</span><span class="p">);</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">global_state_</span><span class="o">-&gt;</span><span class="n">timeline</span><span class="p">.</span><span class="n">Initialized</span><span class="p">())</span> <span class="p">{</span>
      <span class="n">gpu_context_</span><span class="o">-&gt;</span><span class="n">RecordEvent</span><span class="p">(</span><span class="n">gpu_op_context_</span><span class="p">.</span><span class="n">event_queue</span><span class="p">,</span> <span class="n">MEMCPY_OUT_FUSION_BUFFER</span><span class="p">,</span> <span class="o">*</span><span class="n">gpu_op_context_</span><span class="p">.</span><span class="n">stream</span><span class="p">);</span>
    <span class="p">}</span>
  <span class="p">}</span>

  <span class="k">return</span> <span class="n">gpu_op_context_</span><span class="p">.</span><span class="n">FinalizeGPUQueue</span><span class="p">(</span><span class="n">entries</span><span class="p">,</span> <span class="nb">true</span><span class="p">,</span> <span class="n">nccl_op_context_</span><span class="p">.</span><span class="n">error_check_callback_</span><span class="p">);</span>
<span class="p">}</span>

<span class="kt">bool</span> <span class="n">CustomNCCLHierarchicalAllreduce</span><span class="o">::</span><span class="n">Enabled</span><span class="p">(</span><span class="k">const</span> <span class="n">ParameterManager</span><span class="o">&amp;</span> <span class="n">param_manager</span><span class="p">,</span>
                                        <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">TensorTableEntry</span><span class="o">&gt;&amp;</span> <span class="n">entries</span><span class="p">,</span>
                                        <span class="k">const</span> <span class="n">Response</span><span class="o">&amp;</span> <span class="n">response</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">NCCLAllreduce</span><span class="o">::</span><span class="n">Enabled</span><span class="p">(</span><span class="n">param_manager</span><span class="p">,</span> <span class="n">entries</span><span class="p">,</span> <span class="n">response</span><span class="p">))</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="k">return</span> <span class="n">param_manager</span><span class="p">.</span><span class="n">CustomHierarchicalAllreduce</span><span class="p">();</span>
<span class="p">}</span>
<span class="cp">#endif
</span></code></pre></div></div>

<ol>
  <li>Navigate to <code class="language-plaintext highlighter-rouge">paramter_manager.h</code> and <code class="language-plaintext highlighter-rouge">parameter_mangager.cc</code> and add/modify the corresponding <code class="language-plaintext highlighter-rouge">CustomHierarchicalAllreduce</code> function including
    <ul>
      <li><code class="language-plaintext highlighter-rouge">bool HierarchicalAllreduce() const;</code></li>
      <li><code class="language-plaintext highlighter-rouge">void SetHierarchicalAllreduce(bool value, bool fixed=false);</code></li>
      <li><code class="language-plaintext highlighter-rouge">Parameter::ParameterManager()</code></li>
      <li><code class="language-plaintext highlighter-rouge">bool ParameterManger::CustomHierarchicalAllreduce(...)</code></li>
      <li><code class="language-plaintext highlighter-rouge">void ParameterManager::SetHierarchicalAllreduce(...)</code></li>
      <li><code class="language-plaintext highlighter-rouge">ParameterManager::Params ParameterManager::GetParams()</code></li>
      <li><code class="language-plaintext highlighter-rouge">void ParameterManager::SetParams(const Params&amp; newParams)</code></li>
      <li><code class="language-plaintext highlighter-rouge">void ParameterManager::LogParameters(double score)</code></li>
      <li>…</li>
    </ul>
  </li>
  <li>Navigate to <code class="language-plaintext highlighter-rouge">operations.cc</code>. Since the <code class="language-plaintext highlighter-rouge">CustomNCCLHierarchicalAllreduce</code> is triggered at runtime, the order for <code class="language-plaintext highlighter-rouge">CustomNCCLHierarchicalAllreduce</code> should be higher than <code class="language-plaintext highlighter-rouge">NCCLHierarchicalAllreduce</code> such as
    <div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#elif HAVE_NCCL &amp;&amp; HOROVOD_GPU_ALLREDUCE == 'N'
</span> <span class="n">adasum_ops</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">shared_ptr</span><span class="o">&lt;</span><span class="n">AllreduceOp</span><span class="o">&gt;</span><span class="p">(</span><span class="k">new</span> <span class="nf">AdasumGpuAllreduceOp</span><span class="p">(</span><span class="o">&amp;</span><span class="n">mpi_context</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">nccl_context</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">gpu_context</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">state</span><span class="p">)));</span>

 <span class="n">allreduce_ops</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span>
     <span class="n">std</span><span class="o">::</span><span class="n">shared_ptr</span><span class="o">&lt;</span><span class="n">AllreduceOp</span><span class="o">&gt;</span><span class="p">(</span><span class="k">new</span> <span class="nf">NCCLHierarchicalAllreduce</span><span class="p">(</span>
         <span class="o">&amp;</span><span class="n">nccl_context</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">mpi_context</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">gpu_context</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">state</span><span class="p">)));</span>
            
 <span class="n">allreduce_ops</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span>
     <span class="n">std</span><span class="o">::</span><span class="n">shared_ptr</span><span class="o">&lt;</span><span class="n">AllreduceOp</span><span class="o">&gt;</span><span class="p">(</span><span class="k">new</span> <span class="nf">CustomNCCLHierarchicalAllreduce</span><span class="p">(</span>
         <span class="o">&amp;</span><span class="n">nccl_context</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">mpi_context</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">gpu_context</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">state</span><span class="p">)));</span>
</code></pre></div>    </div>
    <p>Also need to set flag for custom hierarchical allreduce.</p>
  </li>
  <li>After configuring C++ code, we can configure the Python <code class="language-plaintext highlighter-rouge">runner</code></li>
  <li>At line 41 of <code class="language-plaintext highlighter-rouge">horovod/horovod/runner/__init__.py</code> add
    <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="bp">self</span><span class="p">.</span><span class="n">custom_hierarchical_allreduce</span> <span class="o">=</span> <span class="bp">None</span>
</code></pre></div>    </div>
  </li>
  <li>At line 328 of <code class="language-plaintext highlighter-rouge">horovod/horovod/runner/lauch.py</code> add
    <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">group_hierarchical_allreduce</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s">'--custom-hierarchical-allreduce'</span><span class="p">,</span>
                                           <span class="n">action</span><span class="o">=</span><span class="n">make_override_true_action</span><span class="p">(</span><span class="n">override_args</span><span class="p">),</span>
                                           <span class="n">help</span><span class="o">=</span><span class="s">'Perform custom hierarchical allreduce between workers instead of '</span>
                                                <span class="s">'ring allreduce. Hierarchical allreduce performs a local '</span>
                                                <span class="s">'allreduce / gather within a host, then a parallel cross allreduce '</span>
                                                <span class="s">'between equal local ranks across workers, and finally a '</span>
                                                <span class="s">'local gather.'</span><span class="p">)</span>
</code></pre></div>    </div>
  </li>
  <li>At line 23 of <code class="language-plaintext highlighter-rouge">horovod/horovod/runner/common/util/config_parser.py</code> add
    <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">CUSTOM_HOROVOD_HIERARCHICAL_ALLREDUCE</span> <span class="o">=</span> <span class="s">'CUSTOM_HOROVOD_HIERARCHICAL_ALLREDUCE'</span>
</code></pre></div>    </div>
    <p>line 87</p>
    <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>     <span class="n">_set_arg_from_config</span><span class="p">(</span><span class="n">args</span><span class="p">,</span> <span class="s">'custom_hierarchical_allreduce'</span><span class="p">,</span> <span class="n">override_args</span><span class="p">,</span> <span class="n">params</span><span class="p">)</span>
</code></pre></div>    </div>
    <p>line 187</p>
    <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">_add_arg_to_env</span><span class="p">(</span><span class="n">env</span><span class="p">,</span> <span class="n">CUSTOM_HOROVOD_HIERARCHICAL_ALLREDUCE</span><span class="p">,</span> <span class="n">args</span><span class="p">.</span><span class="n">custom_hierarchical_allreduce</span><span class="p">,</span> <span class="n">identity</span><span class="p">)</span>
</code></pre></div>    </div>
  </li>
  <li>For running custom hierarchical allreduce operation, use one of the following commands:
    <ul>
      <li><code class="language-plaintext highlighter-rouge">$ NCCL_DEBUG=INFO horovodrun -np 2 --custom-hierarchical-allreduce python train.py</code></li>
      <li><code class="language-plaintext highlighter-rouge">$ mpirun -np 2 -H server1:1, server2:1 -x NCCL_DEBUG=INFO -x CUSTOM_HIERARCHIL_ALLREDUCE=1 python train.py</code></li>
    </ul>
  </li>
</ol>

<p><em>Note: If there are problems relating to the environment, try to set the following environment variables <code class="language-plaintext highlighter-rouge">HOROVOD_NCCL_HOME=&lt;path_to_nccl&gt;</code>, <code class="language-plaintext highlighter-rouge">PATH=$PATH:&lt;path_to_openmpi&gt;</code>,<code class="language-plaintext highlighter-rouge">LD_LIBRARY_PATH=$LD_LIBRARY_PATH:&lt;path_to_nccl&gt;</code></em>
<em>Note2: Add a timeline to record the events is also recommended, set <code class="language-plaintext highlighter-rouge">--timeline-filename=&lt;path_to_timeline_file&gt;</code> for <code class="language-plaintext highlighter-rouge">horovodrun</code> or <code class="language-plaintext highlighter-rouge">-x HOROVOD_TIMELINE=&lt;path_to_timeline_file&gt;</code> for <code class="language-plaintext highlighter-rouge">mpirun</code></em></p>

<h2 id="compilation"><a href="#Compilation">Compilation</a></h2>

<p>To compile the new custom operator, execute the following (coupled with Pytorch for example):</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">rm</span> <span class="nt">-rf</span> build/ dist/
<span class="nv">$ </span>pip uninstall <span class="nt">-y</span> horovod
<span class="nv">$ HOROVOD_NCCL_HOME</span><span class="o">=</span>&lt;path_to_nccl&gt; <span class="nv">HOROVOD_GPU_OPERATIONS</span><span class="o">=</span>NCCL <span class="nv">HOROVOD_WITH_PYTORCH</span><span class="o">=</span>1 <span class="nv">HOROVOD_WITHOUT_TENSORFLOW</span><span class="o">=</span>1 <span class="nv">HOROVOD_WITHOUT_MXNET</span><span class="o">=</span>1 python setup.py <span class="nb">install</span>
</code></pre></div></div>

<p><em>Note: for multiple open-mpi/nccl versions installed, try setting <code class="language-plaintext highlighter-rouge">export PATH=$PATH:&lt;path_to_openmpi&gt;</code> and <code class="language-plaintext highlighter-rouge">export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:&lt;path_to_nccl&gt;</code></em></p>]]></content><author><name></name></author><category term="tutorial" /><category term="horovod" /><category term="distributed" /><category term="machine-learning" /><category term="allreduce" /><category term="algorithm" /><summary type="html"><![CDATA[Prequisites]]></summary></entry><entry><title type="html">Introduction to Hyperledger Fabric</title><link href="https://blog.dex.moe/tutorial/2019/12/29/introduction-to-hyperledger-fabric.html" rel="alternate" type="text/html" title="Introduction to Hyperledger Fabric" /><published>2019-12-29T10:27:04+00:00</published><updated>2019-12-29T10:27:04+00:00</updated><id>https://blog.dex.moe/tutorial/2019/12/29/introduction-to-hyperledger-fabric</id><content type="html" xml:base="https://blog.dex.moe/tutorial/2019/12/29/introduction-to-hyperledger-fabric.html"><![CDATA[<p>Most contents are adapted from <a href="https://www.hyperledger.org/">official hyperledger website</a> and some Chinese materials are from <a href="https://github.com/yeasy/blockchain_guide"><em>blockchain guide</em></a> by <a href="https://yeasy.github.io/">Dr. Baohua Yang</a> (my previous mentor and lead of <em>fabric-sdk-py</em>). This tutorial is also based on my presentation at ZJU blockchain association and slides are available <a href="https://slides.dex.moe">here</a></p>

<hr />

<h2 id="what-is-hyperledger12">What is Hyperledger<sup>1,2</sup>?</h2>

<blockquote>
  <p>Hyperledger is an open source collaborative effort created to advance cross-industry blockchain technologies.</p>
</blockquote>

<p>超级账本(Hyperledger)是一个致力于推动跨产业区块链技术发展的开源平台。依托于Linux基金会，超级账本项目吸引了包括IBM、Intel、Cisco、DAH、摩根大通、R3、甲骨文、百度、腾讯等在内的众多科技和金融巨头的参与贡献。其应用实践囊括了金融、银行、物联网(IoT)、供应链等诸多领域。成立两年多时间以来，超级账本得到了广泛的关注和飞速的发展，目前有近三百家企业会员。超级账本的开源代码和技术，也成为分布式账本领域的首选。</p>

<h2 id="what-is-fabric2">What is Fabric<sup>2</sup>?</h2>

<p><img src="/assets/images/fabric.png" alt="" /></p>

<p>Fabric是一个面向企业商用的分布式账本平台，其创新地引入了权限管理支持，设计上支持可插拔、可扩展，是首个面向联盟链场景的开源项目。</p>

<p>作为最早加入到超级账本项目中的顶级项目，Fabric 由 IBM、DAH 等企业于 2015 年底联合贡献到社区。项目在 Github 上地址为 <a href="https://github.com/hyperledger/fabric">https://github.com/hyperledger/fabric</a>。</p>

<p>Fabric项目基于Go语言实现，贡献者超过160人，总提交次数已经超过15000次，核心代码数超过15万行。</p>

<p>Fabric项目目前处于活跃状态，已发布2.0.0版本，同时包括Fabric CA、Fabric SDK等多个相关的子项目。</p>

<p>项目的邮件列表地址为<a href="mailto:fabric@lists.hyperledger.org">fabric@lists.hyperledger.org</a>。</p>

<h2 id="fabric中的一些关键概念">Fabric中的一些关键概念</h2>

<ul>
  <li>Identity 身份
    <ul>
      <li>区块链网络中的不同参与者包括 Peer 节点、排序节点、客户端应用程序、管理员等等。这些参与者都有一个封装在 X.509 数字证书 (Certificate Authority, CA) 中的数字身份。这些身份决定了参与者在区块链网络中对资源的确切权限和对信息的访问权限。</li>
      <li>Certificate Authority（CA）：负责身份权限管理，又叫 Member Service 或 Identity Service。</li>
    </ul>
  </li>
  <li>Membership Service Provider 成员服务提供者
    <ul>
      <li>识别出哪些根 CA 和中间 CA 是受到信任来定义某个信任领域的成员的，例如，一个组织。</li>
      <li>成员服务的抽象访问接口，实现对不同成员服务的可拔插支持。</li>
    </ul>
  </li>
  <li>Peers 节点
    <ul>
      <li>节点托管着账本和智能合约</li>
      <li>Committer（提交节点）：1.0 架构中一种 peer 节点角色，负责对 orderer 排序后的交易进行检查，选择合法的交易执行并写入存储。</li>
      <li>Endorser（背书节点）：1.0 架构中一种 peer 节点角色，负责检验某个交易是否合法，是否愿意为之背书、签名。</li>
      <li>Orderer（排序节点）：1.0 架构中的共识服务角色，负责排序看到的交易，提供全局确认的顺序。</li>
      <li>Validating Peer（验证节点）：维护账本的核心节点，参与一致性维护、对交易的验证和执行。</li>
      <li>Non-validating Peer（非验证节点）：不参与账本维护，仅作为交易代理响应客户端的 REST 请求，并对交易进行一些基本的有效性检查，之后转发给验证节点。</li>
    </ul>
  </li>
  <li>Smart Contracts and Chaincode 智能合约和链码
    <ul>
      <li>区块链上的应用代码，扩展自“智能合约”概念，支持 golang、nodejs 等，运行在隔离的容器环境中。</li>
      <li>通常，智能合约定义的是控制世界状态中业务对象生命周期的交易逻辑，随后该交易逻辑被打包进链码，紧接着链码会被部署到区块链网络中。可以将智能合约看成交易的管理者，而链码则管理着如何将智能合约打包用于部署。</li>
    </ul>
  </li>
  <li>Ledger 账本
    <ul>
      <li>包括区块链结构（带有所有的可验证交易信息，但只有最终成功的交易会改变世界观）和当前的世界观（world state）。Ledger 仅存在于 Peer 节点。</li>
      <li>World State（世界观）：是一个键值数据库，chaincode 用它来存储交易相关的状态。</li>
    </ul>
  </li>
</ul>

<h2 id="fabric架构设计">Fabric架构设计</h2>

<p>区块链是一个分布式系统，由许多相互通信的节点组成。区块链运行链码来保存状态和账本数据，并执行交易。链码是核心元素，因为交易是调用链码的操作。交易必须“背书”，只有背书的交易才可以提交并对状态产生影响。可能存在一个或多个用于管理方法和参数的特殊链码，称之为<em>系统链码</em>。</p>

<p>Fabric的架构设计有以下几个优势：</p>

<ul>
  <li><strong>链码信任的灵活性。</strong>该架构将链码(区块链应用程序)的<em>信任假设</em>与排序的信任假设分开。换句话说，排序服务可能由一组节点(排序节点)提供，并可以容忍其中一些节点出现故障或不当行为，而且每个链码的背书者可能不同。</li>
  <li><strong>可扩展性。</strong>由于负责特定链码的背书节点与排序节点是无关的，因此系统的<em>扩展性</em>会比由相同的节点完成这些功能更好。特别是当不同的链码指定不同的背书节点时，这会让背书节点的链码互相隔离，并允许并行执行链码（背书）。因此，代价高昂的链码执行从排序服务的关键路径中删除了。</li>
  <li><strong>机密性。</strong>本架构有助于部署对其交易的内容和状态更新具有“机密性”要求的链码。</li>
  <li><strong>共识模块化。</strong>该架构是<em>模块化</em>的，允许可插拔的共识算法（即排序服务）实现。</li>
</ul>

<p>Fabric作为一个分布式账本平台，其中一个核心概念是账本（ledger）。Fabric中账本由两部分组成 - 世界观(world state)和区块链(blockchain)。</p>

<p>整个功能架构如下图所示。</p>

<p><img src="/assets/images/refarch.png" alt="" /></p>

<p>主要有三大组件：区块链服务（Blockchain）、链码服务（Chaincode）、成员权限管理（Membership）。</p>

<h3 id="区块链服务">区块链服务</h3>

<p>区块链服务提供一个分布式账本平台。一般地，多个交易被打包进区块中，多个区块构成一条区块链。区块链代表的是账本状态机发生变更的历史过程。</p>

<h4 id="交易">交易</h4>

<p>交易意味着围绕着某个链码进行操作。 交易可以改变世界状态。在新版本的gRPC服务中交易有交易动作（transaction action）。而交易动作有头文（header）和负载（payload）组成。</p>

<p>其中头文主要包含以下信息：</p>

<ul>
  <li>通道头文：
    <ul>
      <li>version：版本；</li>
      <li>时间戳 timestamp；</li>
      <li>channel_id：通道id；</li>
      <li>tx_id：代表交易的唯一编号；</li>
      <li>epoch：当前周期（头文生成的周期）</li>
      <li>tls_cert_hash：当mutual TLS启用时，客户TLS证书的hash值；</li>
    </ul>
  </li>
  <li>签名头文
    <ul>
      <li>creator：签名身份</li>
      <li>临时生成值 nonce：跟安全机制相关。</li>
    </ul>
  </li>
</ul>

<p>具体来说，交易的数据结构（Protobuf 格式）定义为</p>

<div class="language-protobuf highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">message</span> <span class="nc">Transaction</span> <span class="p">{</span>

    <span class="c1">// The payload is an array of TransactionAction. An array is necessary to</span>
    <span class="c1">// accommodate multiple actions per transaction</span>
    <span class="k">repeated</span> <span class="n">TransactionAction</span> <span class="na">actions</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>

<span class="c1">// TransactionAction binds a proposal to its action.  The type field in the</span>
<span class="c1">// header dictates the type of action to be applied to the ledger.</span>
<span class="kd">message</span> <span class="nc">TransactionAction</span> <span class="p">{</span>

    <span class="c1">// The header of the proposal action, which is the proposal header</span>
    <span class="kt">bytes</span> <span class="na">header</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>

    <span class="c1">// The payload of the action as defined by the type in the header For</span>
    <span class="c1">// chaincode, it's the bytes of ChaincodeActionPayload</span>
    <span class="kt">bytes</span> <span class="na">payload</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>其中 <code class="language-plaintext highlighter-rouge">Header</code> 定义为</p>

<div class="language-protobuf highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">message</span> <span class="nc">Header</span> <span class="p">{</span>
    <span class="kt">bytes</span> <span class="na">channel_header</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
    <span class="kt">bytes</span> <span class="na">signature_header</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
<span class="p">}</span>

<span class="c1">// Header is a generic replay prevention and identity message to include in a signed payload</span>
<span class="kd">message</span> <span class="nc">ChannelHeader</span> <span class="p">{</span>
    <span class="kt">int32</span> <span class="na">type</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="c1">// Header types 0-10000 are reserved and defined by HeaderType</span>

    <span class="c1">// Version indicates message protocol version</span>
    <span class="kt">int32</span> <span class="na">version</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>

    <span class="c1">// Timestamp is the local time when the message was created</span>
    <span class="c1">// by the sender</span>
    <span class="n">google.protobuf.Timestamp</span> <span class="na">timestamp</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span>

    <span class="c1">// Identifier of the channel this message is bound for</span>
    <span class="kt">string</span> <span class="na">channel_id</span> <span class="o">=</span> <span class="mi">4</span><span class="p">;</span>

    <span class="c1">// An unique identifier that is used end-to-end.</span>
    <span class="c1">//  -  set by higher layers such as end user or SDK</span>
    <span class="c1">//  -  passed to the endorser (which will check for uniqueness)</span>
    <span class="c1">//  -  as the header is passed along unchanged, it will be</span>
    <span class="c1">//     be retrieved by the committer (uniqueness check here as well)</span>
    <span class="c1">//  -  to be stored in the ledger</span>
    <span class="kt">string</span> <span class="na">tx_id</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span>

    <span class="c1">// The epoch in which this header was generated, where epoch is defined based on block height</span>
    <span class="c1">// Epoch in which the response has been generated. This field identifies a</span>
    <span class="c1">// logical window of time. A proposal response is accepted by a peer only if</span>
    <span class="c1">// two conditions hold:</span>
    <span class="c1">// 1. the epoch specified in the message is the current epoch</span>
    <span class="c1">// 2. this message has been only seen once during this epoch (i.e. it hasn't</span>
    <span class="c1">//    been replayed)</span>
    <span class="kt">uint64</span> <span class="na">epoch</span> <span class="o">=</span> <span class="mi">6</span><span class="p">;</span>

    <span class="c1">// Extension that may be attached based on the header type</span>
    <span class="kt">bytes</span> <span class="na">extension</span> <span class="o">=</span> <span class="mi">7</span><span class="p">;</span>

    <span class="c1">// If mutual TLS is employed, this represents</span>
    <span class="c1">// the hash of the client's TLS certificate</span>
    <span class="kt">bytes</span> <span class="na">tls_cert_hash</span> <span class="o">=</span> <span class="mi">8</span><span class="p">;</span>
<span class="p">}</span>

<span class="kd">message</span> <span class="nc">SignatureHeader</span> <span class="p">{</span>
    <span class="c1">// Creator of the message, a marshaled msp.SerializedIdentity</span>
    <span class="kt">bytes</span> <span class="na">creator</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>

    <span class="c1">// Arbitrary number that may only be used once. Can be used to detect replay attacks.</span>
    <span class="kt">bytes</span> <span class="na">nonce</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">ChaincodeActionPayload</code>定义为</p>

<div class="language-protobuf highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// ChaincodeAction contains the actions the events generated by the execution</span>
<span class="c1">// of the chaincode.</span>
<span class="kd">message</span> <span class="nc">ChaincodeAction</span> <span class="p">{</span>
    <span class="n">reserved</span> <span class="mi">5</span><span class="p">;</span>
    <span class="n">reserved</span> <span class="s">"token_operations"</span><span class="p">;</span>

    <span class="c1">// This field contains the read set and the write set produced by the</span>
    <span class="c1">// chaincode executing this invocation.</span>
    <span class="kt">bytes</span> <span class="na">results</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>

    <span class="c1">// This field contains the events generated by the chaincode executing this</span>
    <span class="c1">// invocation.</span>
    <span class="kt">bytes</span> <span class="na">events</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>

    <span class="c1">// This field contains the result of executing this invocation.</span>
    <span class="n">Response</span> <span class="na">response</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span>

    <span class="c1">// This field contains the ChaincodeID of executing this invocation. Endorser</span>
    <span class="c1">// will set it with the ChaincodeID called by endorser while simulating proposal.</span>
    <span class="c1">// Committer will validate the version matching with latest chaincode version.</span>
    <span class="c1">// Adding ChaincodeID to keep version opens up the possibility of multiple</span>
    <span class="c1">// ChaincodeAction per transaction.</span>
    <span class="n">ChaincodeID</span> <span class="na">chaincode_id</span> <span class="o">=</span> <span class="mi">4</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<h4 id="交易背书的流程3">交易背书的流程<sup>3</sup></h4>

<p><img src="/assets/images/fabric_tx_flow.png" alt="" /></p>

<p>如图所示，主要是四步：</p>
<ol>
  <li>客户端创建一个交易并将其发送给它所指定的背书节点</li>
  <li>背书节点模拟交易并生成背书签名</li>
  <li>提交客户端收集交易背书并向排序服务广播</li>
  <li>排序服务将交易发送给节点</li>
</ol>

<h4 id="区块">区块</h4>

<p>区块打包交易，确认交易后的世界状态。</p>

<p>一个区块中主要由三部分组成：</p>

<ul>
  <li>区块头 Block Header
    <ul>
      <li>区块数：一个由0开始的整数(int)，0为创世区块</li>
      <li>当前区块的哈希： 这个区块里所有的交易的哈希值</li>
      <li>前一个区块的哈希：前一个区块的哈希值</li>
    </ul>
  </li>
  <li>区块数据 Block Data
    <ul>
      <li>包含交易序列</li>
      <li>在区块生成时被排序服务生成</li>
    </ul>
  </li>
  <li>区块的元数据 Block Metadata
    <ul>
      <li>包含区块生成者的证书和签名（可用来验证）</li>
      <li>这部分并不会用来生成区块哈希值</li>
    </ul>
  </li>
</ul>

<p><img src="/assets/images/ledger-tx.png" alt="" /></p>

<p><em>区块头细节如图所示。</em></p>

<p>区块的数据结构（Protobuf 格式）定义为</p>

<div class="language-protobuf highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">message</span> <span class="nc">Block</span> <span class="p">{</span>
    <span class="n">BlockHeader</span> <span class="na">header</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
    <span class="n">BlockData</span> <span class="na">data</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
    <span class="n">BlockMetadata</span> <span class="na">metadata</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>其中 <code class="language-plaintext highlighter-rouge">BlockHeader</code>, <code class="language-plaintext highlighter-rouge">BlockData</code> , <code class="language-plaintext highlighter-rouge">BlockMetadata</code> 定义为</p>

<div class="language-protobuf highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">message</span> <span class="nc">BlockHeader</span> <span class="p">{</span>
    <span class="kt">uint64</span> <span class="na">number</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="c1">// The position in the blockchain</span>
    <span class="kt">bytes</span> <span class="na">previous_hash</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span> <span class="c1">// The hash of the previous block header</span>
    <span class="kt">bytes</span> <span class="na">data_hash</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span> <span class="c1">// The hash of the BlockData, by MerkleTree</span>
<span class="p">}</span>

<span class="kd">message</span> <span class="nc">BlockData</span> <span class="p">{</span>
    <span class="k">repeated</span> <span class="kt">bytes</span> <span class="na">data</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>

<span class="kd">message</span> <span class="nc">BlockMetadata</span> <span class="p">{</span>
    <span class="k">repeated</span> <span class="kt">bytes</span> <span class="na">metadata</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>一个真实的区块内容示例在本博客<a href="/assets/sample.block">这里下载</a></p>

<h4 id="世界观-world-state">世界观 (World State)</h4>

<p>世界观用于存放链码执行过程中涉及到的状态变量，是一个键值<strong>数据库</strong>。典型的元素为 <code class="language-plaintext highlighter-rouge">{key=K, value=V} version=0</code> 结构。</p>

<p>开发者编写的应用可以启动智能合约使用简单的账本API来获得(get)，放置(put)，删除(delete)状态变量。应用可以上传交易使得世界观存贮的键值发生变化，其中应用仅仅是上传交易，和得到交易是否成功（是否有足够多的背书节点同意变化并签名）的返回，这些应用并不会参与共识机制。</p>

<p>世界观的键值中包含了一个<code class="language-plaintext highlighter-rouge">version</code>，这个数字只用于fabric内部，并随着状态变量的改动而递增(+1)。当状态更新后，这个版本号会被用来验证状态是否更新成功。</p>

<h3 id="链码服务">链码服务</h3>

<p>链码包含所有的处理逻辑，并对外提供接口，外部通过调用链码接口来改变世界观。</p>

<h4 id="接口和操作">接口和操作</h4>

<p>链码有两个主要接口（Interface），一个是 <code class="language-plaintext highlighter-rouge">Chaincode</code> 接口，以接收交易信息（Transaction），另一个是 <code class="language-plaintext highlighter-rouge">ChaincodeStubInterface</code> 接口，用来获取、修改账本信息和在不同链码间的服务调用。目前接口实现有 Golang, Javascript, Java 三种语言。</p>

<p>一个样例的链码如下所示。</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">package</span> <span class="n">main</span>

<span class="k">import</span> <span class="p">(</span>
    <span class="s">"fmt"</span>

    <span class="s">"github.com/hyperledger/fabric/core/chaincode/shim"</span>
    <span class="s">"github.com/hyperledger/fabric/protos/peer"</span>
<span class="p">)</span>

<span class="c">// SimpleAsset implements a simple chaincode to manage an asset</span>
<span class="k">type</span> <span class="n">SimpleAsset</span> <span class="k">struct</span> <span class="p">{</span>
<span class="p">}</span>

<span class="c">// Init is called during chaincode instantiation to initialize any</span>
<span class="c">// data. Note that chaincode upgrade also calls this function to reset</span>
<span class="c">// or to migrate data.</span>
<span class="k">func</span> <span class="p">(</span><span class="n">t</span> <span class="o">*</span><span class="n">SimpleAsset</span><span class="p">)</span> <span class="n">Init</span><span class="p">(</span><span class="n">stub</span> <span class="n">shim</span><span class="o">.</span><span class="n">ChaincodeStubInterface</span><span class="p">)</span> <span class="n">peer</span><span class="o">.</span><span class="n">Response</span> <span class="p">{</span>
    <span class="c">// Get the args from the transaction proposal</span>
    <span class="n">args</span> <span class="o">:=</span> <span class="n">stub</span><span class="o">.</span><span class="n">GetStringArgs</span><span class="p">()</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">args</span><span class="p">)</span> <span class="o">!=</span> <span class="m">2</span> <span class="p">{</span>
            <span class="k">return</span> <span class="n">shim</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="s">"Incorrect arguments. Expecting a key and a value"</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="c">// Set up any variables or assets here by calling stub.PutState()</span>

    <span class="c">// We store the key and the value on the ledger</span>
    <span class="n">err</span> <span class="o">:=</span> <span class="n">stub</span><span class="o">.</span><span class="n">PutState</span><span class="p">(</span><span class="n">args</span><span class="p">[</span><span class="m">0</span><span class="p">],</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">(</span><span class="n">args</span><span class="p">[</span><span class="m">1</span><span class="p">]))</span>
    <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
            <span class="k">return</span> <span class="n">shim</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"Failed to create asset: %s"</span><span class="p">,</span> <span class="n">args</span><span class="p">[</span><span class="m">0</span><span class="p">]))</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="n">shim</span><span class="o">.</span><span class="n">Success</span><span class="p">(</span><span class="no">nil</span><span class="p">)</span>
<span class="p">}</span>

<span class="c">// Invoke is called per transaction on the chaincode. Each transaction is</span>
<span class="c">// either a 'get' or a 'set' on the asset created by Init function. The Set</span>
<span class="c">// method may create a new asset by specifying a new key-value pair.</span>
<span class="k">func</span> <span class="p">(</span><span class="n">t</span> <span class="o">*</span><span class="n">SimpleAsset</span><span class="p">)</span> <span class="n">Invoke</span><span class="p">(</span><span class="n">stub</span> <span class="n">shim</span><span class="o">.</span><span class="n">ChaincodeStubInterface</span><span class="p">)</span> <span class="n">peer</span><span class="o">.</span><span class="n">Response</span> <span class="p">{</span>
    <span class="c">// Extract the function and args from the transaction proposal</span>
    <span class="n">fn</span><span class="p">,</span> <span class="n">args</span> <span class="o">:=</span> <span class="n">stub</span><span class="o">.</span><span class="n">GetFunctionAndParameters</span><span class="p">()</span>

    <span class="k">var</span> <span class="n">result</span> <span class="kt">string</span>
    <span class="k">var</span> <span class="n">err</span> <span class="kt">error</span>
    <span class="k">if</span> <span class="n">fn</span> <span class="o">==</span> <span class="s">"set"</span> <span class="p">{</span>
            <span class="n">result</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">set</span><span class="p">(</span><span class="n">stub</span><span class="p">,</span> <span class="n">args</span><span class="p">)</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="c">// assume 'get' even if fn is nil</span>
            <span class="n">result</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">get</span><span class="p">(</span><span class="n">stub</span><span class="p">,</span> <span class="n">args</span><span class="p">)</span>
    <span class="p">}</span>
    <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
            <span class="k">return</span> <span class="n">shim</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">err</span><span class="o">.</span><span class="n">Error</span><span class="p">())</span>
    <span class="p">}</span>

    <span class="c">// Return the result as success payload</span>
    <span class="k">return</span> <span class="n">shim</span><span class="o">.</span><span class="n">Success</span><span class="p">([]</span><span class="kt">byte</span><span class="p">(</span><span class="n">result</span><span class="p">))</span>
<span class="p">}</span>

<span class="c">// Set stores the asset (both key and value) on the ledger. If the key exists,</span>
<span class="c">// it will override the value with the new one</span>
<span class="k">func</span> <span class="n">set</span><span class="p">(</span><span class="n">stub</span> <span class="n">shim</span><span class="o">.</span><span class="n">ChaincodeStubInterface</span><span class="p">,</span> <span class="n">args</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="p">(</span><span class="kt">string</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">args</span><span class="p">)</span> <span class="o">!=</span> <span class="m">2</span> <span class="p">{</span>
            <span class="k">return</span> <span class="s">""</span><span class="p">,</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Errorf</span><span class="p">(</span><span class="s">"Incorrect arguments. Expecting a key and a value"</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="n">err</span> <span class="o">:=</span> <span class="n">stub</span><span class="o">.</span><span class="n">PutState</span><span class="p">(</span><span class="n">args</span><span class="p">[</span><span class="m">0</span><span class="p">],</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">(</span><span class="n">args</span><span class="p">[</span><span class="m">1</span><span class="p">]))</span>
    <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
            <span class="k">return</span> <span class="s">""</span><span class="p">,</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Errorf</span><span class="p">(</span><span class="s">"Failed to set asset: %s"</span><span class="p">,</span> <span class="n">args</span><span class="p">[</span><span class="m">0</span><span class="p">])</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="n">args</span><span class="p">[</span><span class="m">1</span><span class="p">],</span> <span class="no">nil</span>
<span class="p">}</span>

<span class="c">// Get returns the value of the specified asset key</span>
<span class="k">func</span> <span class="n">get</span><span class="p">(</span><span class="n">stub</span> <span class="n">shim</span><span class="o">.</span><span class="n">ChaincodeStubInterface</span><span class="p">,</span> <span class="n">args</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="p">(</span><span class="kt">string</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">args</span><span class="p">)</span> <span class="o">!=</span> <span class="m">1</span> <span class="p">{</span>
            <span class="k">return</span> <span class="s">""</span><span class="p">,</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Errorf</span><span class="p">(</span><span class="s">"Incorrect arguments. Expecting a key"</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="n">value</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">stub</span><span class="o">.</span><span class="n">GetState</span><span class="p">(</span><span class="n">args</span><span class="p">[</span><span class="m">0</span><span class="p">])</span>
    <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
            <span class="k">return</span> <span class="s">""</span><span class="p">,</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Errorf</span><span class="p">(</span><span class="s">"Failed to get asset: %s with error: %s"</span><span class="p">,</span> <span class="n">args</span><span class="p">[</span><span class="m">0</span><span class="p">],</span> <span class="n">err</span><span class="p">)</span>
    <span class="p">}</span>
    <span class="k">if</span> <span class="n">value</span> <span class="o">==</span> <span class="no">nil</span> <span class="p">{</span>
            <span class="k">return</span> <span class="s">""</span><span class="p">,</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Errorf</span><span class="p">(</span><span class="s">"Asset not found: %s"</span><span class="p">,</span> <span class="n">args</span><span class="p">[</span><span class="m">0</span><span class="p">])</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="kt">string</span><span class="p">(</span><span class="n">value</span><span class="p">),</span> <span class="no">nil</span>
<span class="p">}</span>

<span class="c">// main function starts up the chaincode in the container during instantiate</span>
<span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">if</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">shim</span><span class="o">.</span><span class="n">Start</span><span class="p">(</span><span class="nb">new</span><span class="p">(</span><span class="n">SimpleAsset</span><span class="p">));</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
            <span class="n">fmt</span><span class="o">.</span><span class="n">Printf</span><span class="p">(</span><span class="s">"Error starting SimpleAsset chaincode: %s"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="成员权限管理">成员权限管理</h3>

<p>通过基于 PKI 的成员权限管理，平台可以对接入的节点和客户端的能力进行限制。</p>

<p><img src="/assets/images/msp-diagram.png" alt="" /></p>

<p>MSP主要如图所示，主要由以下几个组成：</p>
<ul>
  <li>根 CA：包含根CA自主签名的X.509证书列表；</li>
  <li>中间 CA：包含受这个组织信任的中间 CA 对应的 X.509 证书列表；</li>
  <li>组织单元 （OU）：包含组织单元的一个列表；</li>
  <li>管理员：该文件夹包含了一个身份列表，其中的身份为该组织定义了哪些操作者担任管理员；</li>
  <li>撤销证书：包含被撤销身份的参与者；</li>
  <li>节点身份：包含节点的 X.509 证书用来在网络中验证身份；</li>
  <li>私钥：根据 Peer 节点或排序节点的本地MSP定义包含节点的签名私钥；</li>
  <li>TLS 根 CA：TLS通信时使用的根CA自主签名的X.509证书列表；</li>
  <li>TLS 中间 CA：TLS通信时使用的这个 MSP 所代表的的组织所信任的中间 CA 证书列表。</li>
</ul>

<h2 id="fabric-python-sdk的使用">Fabric Python SDK的使用</h2>

<p>详见本博客另一篇博文： <a href="/fabric/2018/11/12/fabric-sdk-py-operations.html">使用fabric-sdk-py对网络操作</a></p>

<hr />

<h2 id="references">References</h2>

<ol>
  <li><a href="https://hyperledger-fabric.readthedocs.io/">Hyperledger Docs</a></li>
  <li><a href="https://github.com/yeasy/blockchain_guide">Blockchain Guide</a></li>
  <li>Androulaki, E., Barger, A., Bortnikov, V., Cachin, C., Christidis, K., De Caro, A., Enyeart, D., Ferris, C., Laventman, G., Manevich, Y. and Muralidharan, S., 2018, April. Hyperledger fabric: a distributed operating system for permissioned blockchains. In <em>Proceedings of the Thirteenth EuroSys Conference</em> (p. 30). ACM. <a href="https://dl.acm.org/doi/10.1145/3190508.3190538">available here</a></li>
</ol>]]></content><author><name></name></author><category term="tutorial" /><category term="fabric" /><summary type="html"><![CDATA[Most contents are adapted from official hyperledger website and some Chinese materials are from blockchain guide by Dr. Baohua Yang (my previous mentor and lead of fabric-sdk-py). This tutorial is also based on my presentation at ZJU blockchain association and slides are available here]]></summary></entry><entry><title type="html">Understanding configuration files in Fabric</title><link href="https://blog.dex.moe/note/2019/02/16/understanding-docker-compose-files-in-fabric.html" rel="alternate" type="text/html" title="Understanding configuration files in Fabric" /><published>2019-02-16T14:18:45+00:00</published><updated>2019-02-16T14:18:45+00:00</updated><id>https://blog.dex.moe/note/2019/02/16/understanding-docker-compose-files-in-fabric</id><content type="html" xml:base="https://blog.dex.moe/note/2019/02/16/understanding-docker-compose-files-in-fabric.html"><![CDATA[<p>Many times I see docker compose files used in Fabric but since I am new to both docker (container) and fabric (blockchain), I find it difficult to understand why these configuration works and which settings should I change in order to make the network running or functioning accordingly. This post is a summary of what I have learned.</p>

<h1 id="file-extensions">File Extensions</h1>

<h3 id="yaml-yml">.yaml (.yml)</h3>

<blockquote>
  <p>YAML (YAML Ain’t Markup Language) is a human-readable data serialization language<sup>1</sup></p>
</blockquote>

<blockquote>
  <p>It’s a strict superset of JSON, with the addition of syntactically significant newlines and indentation, like Python. Unlike Python, however, YAML doesn’t allow literal tab characters for indentation.<sup>2</sup></p>
</blockquote>

<h3 id="json">.json</h3>

<blockquote>
  <p>JSON (JavaScript Object Notation) is an open-standard file format that uses human-readable text to transmit data objects consisting of attribute-value pairs and array data types (or any other serializable value).<sup>3</sup></p>
</blockquote>

<h1 id="docker-compose">Docker Compose</h1>

<blockquote>
  <p>The Compose file is a YAML file defining services, networks, and volumes for a Docker application.<sup>4</sup></p>
</blockquote>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>version: '3'
services:
  web:
    build: .
    ports:
    - "5000:5000"
    volumes:
    - .:/code
    - logvolume01:/var/log
    links:
    - redis
  redis:
    image: redis
volumes:
  logvolume01: {}
</code></pre></div></div>

<h1 id="examples">Examples</h1>

<ul>
  <li>base (<a href="https://github.com/hyperledger/fabric-samples/blob/master/first-network/base/docker-compose-base.yaml">source files</a>)</li>
  <li>cli</li>
  <li>couch</li>
  <li>e2e-template</li>
  <li>kafka</li>
  <li>org3</li>
</ul>

<h1 id="keys-with-reference">Keys (with reference)</h1>

<ul>
  <li><a href="https://github.com/docker/docker.github.io/blob/master/compose/compose-file/compose-versioning.md#compatibility-matrix"><code class="language-plaintext highlighter-rouge">version</code></a></li>
  <li><a href="https://github.com/docker/docker.github.io/blob/master/compose/compose-file/index.md#service-configuration-reference"><code class="language-plaintext highlighter-rouge">service</code></a></li>
  <li><a href="https://github.com/docker/docker.github.io/blob/master/compose/compose-file/compose-file-v2.md#volume-configuration-reference"><code class="language-plaintext highlighter-rouge">volume</code></a>: mount host folders</li>
</ul>

<hr />

<ol>
  <li><a href="https://en.wikipedia.org/wiki/YAML">Wiki page of <code class="language-plaintext highlighter-rouge">yaml</code></a></li>
  <li><a href="https://learnxinyminutes.com/docs/yaml/">Learn yaml in Y minutes</a></li>
  <li><a href="https://en.wikipedia.org/wiki/JSON">Wiki page of <code class="language-plaintext highlighter-rouge">json</code></a></li>
</ol>]]></content><author><name></name></author><category term="note" /><category term="docker" /><category term="configuration" /><summary type="html"><![CDATA[Many times I see docker compose files used in Fabric but since I am new to both docker (container) and fabric (blockchain), I find it difficult to understand why these configuration works and which settings should I change in order to make the network running or functioning accordingly. This post is a summary of what I have learned.]]></summary></entry><entry><title type="html">Fault Tolerant System 101</title><link href="https://blog.dex.moe/note/2019/02/16/fault-tolerance.html" rel="alternate" type="text/html" title="Fault Tolerant System 101" /><published>2019-02-16T14:07:13+00:00</published><updated>2019-02-16T14:07:13+00:00</updated><id>https://blog.dex.moe/note/2019/02/16/fault-tolerance</id><content type="html" xml:base="https://blog.dex.moe/note/2019/02/16/fault-tolerance.html"><![CDATA[<h1 id="fault-tolerant-system-101">Fault Tolerant System 101</h1>

<p>TBC</p>]]></content><author><name></name></author><category term="note" /><category term="fault-tolerant" /><summary type="html"><![CDATA[Fault Tolerant System 101]]></summary></entry><entry><title type="html">Use SM2/SM3 in Hyperledger Fabric 1.4</title><link href="https://blog.dex.moe/note/2019/02/13/convert-secret-algorithm-in-fabric.html" rel="alternate" type="text/html" title="Use SM2/SM3 in Hyperledger Fabric 1.4" /><published>2019-02-13T02:24:24+00:00</published><updated>2019-02-13T02:24:24+00:00</updated><id>https://blog.dex.moe/note/2019/02/13/convert-secret-algorithm-in-fabric</id><content type="html" xml:base="https://blog.dex.moe/note/2019/02/13/convert-secret-algorithm-in-fabric.html"><![CDATA[<p>To understand why we want to change the default encryption method, we need to have a basic understanding of <strong>openssl</strong>, <strong>gmssl</strong> &amp; <strong>bccsp</strong>.</p>

<h1 id="background">Background</h1>

<h2 id="openssl">OPENSSL</h2>

<p><em><a href="https://www.openssl.org/">official page for <code class="language-plaintext highlighter-rouge">openssl</code></a></em></p>

<h3 id="how-to-check-the-certificate">How to check the certificate</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>openssl x509 -in &lt;&gt; -inform pem -noout -text
</code></pre></div></div>

<h3 id="lexicons-in-openssl">Lexicons in OPENSSL</h3>

<ul>
  <li><code class="language-plaintext highlighter-rouge">.csr</code> (Certificate Signing Request) - a block of encoded text that is given to a Certificate Authority when applying for an SSL Certificate</li>
  <li><code class="language-plaintext highlighter-rouge">.der</code></li>
  <li><code class="language-plaintext highlighter-rouge">.key</code></li>
  <li><code class="language-plaintext highlighter-rouge">.p12</code></li>
  <li><code class="language-plaintext highlighter-rouge">.pfx</code></li>
</ul>

<h2 id="gmssl">GMSSL</h2>

<p><em><a href="http://gmssl.org/">official page for <code class="language-plaintext highlighter-rouge">gmssl</code></a></em></p>

<h3 id="how-to-check-the-certificate-1">How to check the certificate</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gmssl x509 -in &lt;&gt; -inform pem -noout -text
</code></pre></div></div>

<h2 id="bccsp">BCCSP</h2>

<p><em>The interface of bccsp can be found <a href="https://github.com/hyperledger/fabric/blob/release-1.4/bccsp/bccsp.go#L98-L144">there</a></em></p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">type</span> <span class="n">BCCSP</span> <span class="k">interface</span> <span class="p">{</span>

	<span class="c">// KeyGen generates a key using opts.</span>
	<span class="n">KeyGen</span><span class="p">(</span><span class="n">opts</span> <span class="n">KeyGenOpts</span><span class="p">)</span> <span class="p">(</span><span class="n">k</span> <span class="n">Key</span><span class="p">,</span> <span class="n">err</span> <span class="kt">error</span><span class="p">)</span>

	<span class="c">// KeyDeriv derives a key from k using opts.</span>
	<span class="c">// The opts argument should be appropriate for the primitive used.</span>
	<span class="n">KeyDeriv</span><span class="p">(</span><span class="n">k</span> <span class="n">Key</span><span class="p">,</span> <span class="n">opts</span> <span class="n">KeyDerivOpts</span><span class="p">)</span> <span class="p">(</span><span class="n">dk</span> <span class="n">Key</span><span class="p">,</span> <span class="n">err</span> <span class="kt">error</span><span class="p">)</span>

	<span class="c">// KeyImport imports a key from its raw representation using opts.</span>
	<span class="c">// The opts argument should be appropriate for the primitive used.</span>
	<span class="n">KeyImport</span><span class="p">(</span><span class="n">raw</span> <span class="k">interface</span><span class="p">{},</span> <span class="n">opts</span> <span class="n">KeyImportOpts</span><span class="p">)</span> <span class="p">(</span><span class="n">k</span> <span class="n">Key</span><span class="p">,</span> <span class="n">err</span> <span class="kt">error</span><span class="p">)</span>

	<span class="c">// GetKey returns the key this CSP associates to</span>
	<span class="c">// the Subject Key Identifier ski.</span>
	<span class="n">GetKey</span><span class="p">(</span><span class="n">ski</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">)</span> <span class="p">(</span><span class="n">k</span> <span class="n">Key</span><span class="p">,</span> <span class="n">err</span> <span class="kt">error</span><span class="p">)</span>

	<span class="c">// Hash hashes messages msg using options opts.</span>
	<span class="c">// If opts is nil, the default hash function will be used.</span>
	<span class="n">Hash</span><span class="p">(</span><span class="n">msg</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">,</span> <span class="n">opts</span> <span class="n">HashOpts</span><span class="p">)</span> <span class="p">(</span><span class="n">hash</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">,</span> <span class="n">err</span> <span class="kt">error</span><span class="p">)</span>

	<span class="c">// GetHash returns and instance of hash.Hash using options opts.</span>
	<span class="c">// If opts is nil, the default hash function will be returned.</span>
	<span class="n">GetHash</span><span class="p">(</span><span class="n">opts</span> <span class="n">HashOpts</span><span class="p">)</span> <span class="p">(</span><span class="n">h</span> <span class="n">hash</span><span class="o">.</span><span class="n">Hash</span><span class="p">,</span> <span class="n">err</span> <span class="kt">error</span><span class="p">)</span>

	<span class="c">// Sign signs digest using key k.</span>
	<span class="c">// The opts argument should be appropriate for the algorithm used.</span>
	<span class="c">//</span>
	<span class="c">// Note that when a signature of a hash of a larger message is needed,</span>
	<span class="c">// the caller is responsible for hashing the larger message and passing</span>
	<span class="c">// the hash (as digest).</span>
	<span class="n">Sign</span><span class="p">(</span><span class="n">k</span> <span class="n">Key</span><span class="p">,</span> <span class="n">digest</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">,</span> <span class="n">opts</span> <span class="n">SignerOpts</span><span class="p">)</span> <span class="p">(</span><span class="n">signature</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">,</span> <span class="n">err</span> <span class="kt">error</span><span class="p">)</span>

	<span class="c">// Verify verifies signature against key k and digest</span>
	<span class="c">// The opts argument should be appropriate for the algorithm used.</span>
	<span class="n">Verify</span><span class="p">(</span><span class="n">k</span> <span class="n">Key</span><span class="p">,</span> <span class="n">signature</span><span class="p">,</span> <span class="n">digest</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">,</span> <span class="n">opts</span> <span class="n">SignerOpts</span><span class="p">)</span> <span class="p">(</span><span class="n">valid</span> <span class="kt">bool</span><span class="p">,</span> <span class="n">err</span> <span class="kt">error</span><span class="p">)</span>

	<span class="c">// Encrypt encrypts plaintext using key k.</span>
	<span class="c">// The opts argument should be appropriate for the algorithm used.</span>
	<span class="n">Encrypt</span><span class="p">(</span><span class="n">k</span> <span class="n">Key</span><span class="p">,</span> <span class="n">plaintext</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">,</span> <span class="n">opts</span> <span class="n">EncrypterOpts</span><span class="p">)</span> <span class="p">(</span><span class="n">ciphertext</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">,</span> <span class="n">err</span> <span class="kt">error</span><span class="p">)</span>

	<span class="c">// Decrypt decrypts ciphertext using key k.</span>
	<span class="c">// The opts argument should be appropriate for the algorithm used.</span>
	<span class="n">Decrypt</span><span class="p">(</span><span class="n">k</span> <span class="n">Key</span><span class="p">,</span> <span class="n">ciphertext</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">,</span> <span class="n">opts</span> <span class="n">DecrypterOpts</span><span class="p">)</span> <span class="p">(</span><span class="n">plaintext</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">,</span> <span class="n">err</span> <span class="kt">error</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="lexicon-for-bccsp">Lexicon for BCCSP</h3>

<ul>
  <li><code class="language-plaintext highlighter-rouge">ski</code>: subject key identifier</li>
  <li><code class="language-plaintext highlighter-rouge">csp</code>: cryptographic service provider</li>
  <li><code class="language-plaintext highlighter-rouge">hsm</code>: Hardware security module - a physical computing device that safeguards and manages digital keys for strong authentication and provides crypto processing<sup>1</sup></li>
  <li><code class="language-plaintext highlighter-rouge">X.509</code> - a standard defining the format of public key certificates<sup>2</sup> (This is also used in MSP/Membership Service Provider)</li>
  <li><code class="language-plaintext highlighter-rouge">public key certificate</code>/<code class="language-plaintext highlighter-rouge">digital certificate</code>/<code class="language-plaintext highlighter-rouge">identity certificate</code> - an electronic document used to provde the ownership of a public key<sup>3</sup></li>
  <li><code class="language-plaintext highlighter-rouge">public key cryptography</code> <sup>4</sup> - a cryptographic system that uses pairs of keys: <em>public keys</em> which may be disseminated widely, and private keys which are known only to the owner<sup>5</sup>.</li>
  <li><code class="language-plaintext highlighter-rouge">PKI</code>: Public-key infrastructure</li>
  <li><code class="language-plaintext highlighter-rouge">ecdsa</code>: Elliptic Curve Digital Signature Algorithm -</li>
</ul>

<h1 id="steps-to-add-sm2sm3-support-for-fabric">Steps to add sm2/sm3 support for Fabric</h1>

<ol>
  <li>Clone the Hyperledger/fabric under <code class="language-plaintext highlighter-rouge">GOPATH</code></li>
</ol>

<p>For example,</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mkdir -p ~/gopath/src/github.com/hyperledger/fabric
cd ~/gopath/src/github.com/hyperledger/fabric

# OPTIONAL, if not exist
git init
git remote add origin git@github.com:hyperledger/fabric.git
git fetch --all
git checkout release-1.4.0
# END OPTIONAL
</code></pre></div></div>

<p><img src="/assets/images/sm_convertion/git_checkout.png" alt="" /></p>

<ol>
  <li>apply the patch</li>
</ol>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone git@github.com:flyinox/fabric-sm-patch.git
git am fabric-sm-patch/fabric-sm-patch
</code></pre></div></div>

<p>Make sure the HEAD is correct,
<img src="/assets/images/sm_convertion/git_log_head.png" alt="" /></p>

<ol>
  <li>compile to executable</li>
</ol>

<p>Note: You can also check the target compiling file by <code class="language-plaintext highlighter-rouge">go list -f ''</code> under <code class="language-plaintext highlighter-rouge">common/tools/cryptogen</code>
<img src="/assets/images/sm_convertion/check_target.png" alt="" /></p>

<p>But you can also build the file directly by,</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># under &lt;FABRIC&gt;/common/tools/cryptogen
go build mainsm.go
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>make native
</code></pre></div></div>
<p><img src="/assets/images/sm_convertion/make_native.png" alt="" /></p>

<ol>
  <li>generate <em>crypto-config</em></li>
</ol>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># USE cryptogen to generate certificates with sm2/sm3 signature
.build/bin/cryptogen generate --help
</code></pre></div></div>

<p>You should see the following message,
<img src="/assets/images/sm_convertion/generate_help.png" alt="" /></p>

<p>An example certificate for orderer,
<img src="/assets/images/sm_convertion/ex_cert_orderer.png" alt="" /></p>

<p>Or Admin,
<img src="/assets/images/sm_convertion/cert_admin.png" alt="" /></p>

<p>Or MSP CA,
<img src="/assets/images/sm_convertion/cert_msp_ca.png" alt="" /></p>

<p>However, for <strong>tlsca</strong> the signature algorithm is NOT modified,
<img src="/assets/images/sm_convertion/cert_tls.png" alt="" /></p>

<hr />

<ol>
  <li><a href="https://en.wikipedia.org/wiki/Hardware_security_module">Wiki of <code class="language-plaintext highlighter-rouge">hsm</code></a></li>
  <li><a href="https://en.wikipedia.org/wiki/X.509">Wiki of <code class="language-plaintext highlighter-rouge">x509</code></a></li>
  <li><a href="https://en.wikipedia.org/wiki/Public_key_certificate">Wiki of <code class="language-plaintext highlighter-rouge">public key certificate</code></a></li>
  <li><a href="https://en.wikipedia.org/wiki/Key_authentication">Wiki of <code class="language-plaintext highlighter-rouge">key authentication</code></a></li>
  <li><a href="https://en.wikipedia.org/wiki/Public-key_cryptography">Wiki of <code class="language-plaintext highlighter-rouge">public-key cryptography</code></a></li>
</ol>]]></content><author><name></name></author><category term="note" /><category term="fabric" /><category term="encryption" /><category term="cryptography" /><category term="gmssl" /><summary type="html"><![CDATA[To understand why we want to change the default encryption method, we need to have a basic understanding of openssl, gmssl &amp; bccsp.]]></summary></entry><entry><title type="html">Hyperledger Fabric with Kafka and Zookeeper MultiNode Cluster Setup</title><link href="https://blog.dex.moe/note/2019/02/13/fabric-with-kafka-and-zookeeper.html" rel="alternate" type="text/html" title="Hyperledger Fabric with Kafka and Zookeeper MultiNode Cluster Setup" /><published>2019-02-13T00:39:16+00:00</published><updated>2019-02-13T00:39:16+00:00</updated><id>https://blog.dex.moe/note/2019/02/13/fabric-with-kafka-and-zookeeper</id><content type="html" xml:base="https://blog.dex.moe/note/2019/02/13/fabric-with-kafka-and-zookeeper.html"><![CDATA[<p>Today we are going to investigate how to set up a fabric network with kafka and zookeeper. As a beginner, first we need to understand what is kafka and zookeeper.</p>

<h1 id="background">Background</h1>

<h2 id="apache-kafka">Apache Kafka</h2>

<blockquote>
  <p>Apache Kafka is an open-source stream-processing software platform<sup>0</sup>.</p>
</blockquote>

<blockquote>
  <p>Kafka is a distributed, horizontally-saclable, fault-tolerant, commit log<sup>1</sup>.</p>
</blockquote>

<p>Since commit log is <strong>a record of transactions</strong>, Kafka can easily integrates with blockchain platform. Therefore, I think this is the reason why the first “consensus” of Fabric is utilising Kafka while the next would be based on Raft<sup>2</sup>.</p>

<h2 id="zookeeper">Zookeeper</h2>

<p>Zookeeper is a general purpose distribtued process coordination system, it helps to coordinate tasks, manage state, configure, etc accross a distributed system.</p>

<h2 id="ordering-service">Ordering Service</h2>

<p>Ordering Service consists of orderers, providing <code class="language-plaintext highlighter-rouge">a shared communication channel</code> to clients and peers and offering a broadcast service for messages containing transactions.</p>

<p>OSN (Ordering Service Node/ Orderer Node): connected with the endorsers and peers</p>

<h3 id="understanding-the-role-of-ordering-service-in-fabric-network">Understanding the role of ordering service in fabric network</h3>

<p><img src="/assets/images/fabric_tx_flow.png" alt="Fabric transaction flow" /></p>

<p><em>Image Source: <a href="https://arxiv.org/pdf/1801.10228.pdf">Hyperledger Fabric: A Distributed Operating System for Permissioned Blockchains</a></em></p>

<h1 id="configurations">Configurations</h1>

<ul>
  <li>For kafka &amp; zookeep, refer  to <a href="https://github.com/hyperledger/fabric/blob/release-1.4/orderer/common/server/docker-compose.yml">this</a></li>
</ul>

<h1 id="deployment">Deployment</h1>

<p><em>Disclaimer: this is based on <a href="https://github.com/hyperledger/fabric-samples/tree/release-1.4/first-network">fabric-samples/first-network</a></em></p>

<h1 id="faq">FAQ</h1>

<ol>
  <li>If kafka is a streaming-process software, why are we calling kafka-consensus in fabric?</li>
</ol>

<ul>
  <li>First, we need to understand what is consensus. The definition of <code class="language-plaintext highlighter-rouge">consensus</code> is <code class="language-plaintext highlighter-rouge">a general agreement</code><sup>3</sup> and in the context of blockchain, it means <strong>all nodes must agree to the same order of transactions/blocks</strong> to achieve a universal shared ledger</li>
  <li>Kafka provides the same order of transactions among multiple nodes by a shared queue as ordering nodes send to Kafka transactions and receive from Kafka transactions in the same order.</li>
</ul>

<hr />

<ol>
  <li><a href="https://en.wikipedia.org/wiki/Apache_Kafka">Wiki page of <code class="language-plaintext highlighter-rouge">kafka</code></a></li>
  <li><a href="https://hackernoon.com/thorough-introduction-to-apache-kafka-6fbf2989bbc1">Through INtroduction to Apache Kafka</a></li>
  <li><a href="https://docs.google.com/document/d/138Brlx2BiYJm5bzFk_B0csuEUKYdXXr7Za9V7C76dwo/edit">Fabric Proposal: A Raft-Based Ordering Service</a></li>
  <li><a href="https://www.google.com/search?q=consensus">Search Result of <code class="language-plaintext highlighter-rouge">consensus</code></a></li>
</ol>]]></content><author><name></name></author><category term="note" /><category term="fabric" /><category term="kafka" /><category term="zookeeper" /><summary type="html"><![CDATA[Today we are going to investigate how to set up a fabric network with kafka and zookeeper. As a beginner, first we need to understand what is kafka and zookeeper.]]></summary></entry><entry><title type="html">使用fabric-sdk-py对网络操作</title><link href="https://blog.dex.moe/fabric/2018/11/12/fabric-sdk-py-operations.html" rel="alternate" type="text/html" title="使用fabric-sdk-py对网络操作" /><published>2018-11-12T03:54:18+00:00</published><updated>2018-11-12T03:54:18+00:00</updated><id>https://blog.dex.moe/fabric/2018/11/12/fabric-sdk-py-operations</id><content type="html" xml:base="https://blog.dex.moe/fabric/2018/11/12/fabric-sdk-py-operations.html"><![CDATA[<p><em>本文基于<a href="https://github.com/hyperledger/fabric-sdk-py/blob/master/docs/tutorial.md">fabric-sdk-py官方文档</a>翻译，如有更多疑问，请移步至Rocketchat #fabric-sdk-py，谢谢！</em></p>

<h2 id="0操作前准备">0.操作前准备</h2>

<h3 id="01安装-fabric-sdk">0.1　安装 Fabric SDK</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pip <span class="nb">install </span>fabric-sdk-py
</code></pre></div></div>

<p>安装后可以检测sdk版本</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>python
<span class="o">&gt;&gt;&gt;</span> import hfc
<span class="o">&gt;&gt;&gt;</span> print<span class="o">(</span>hfc.VERSION<span class="o">)</span>
0.8.1
</code></pre></div></div>

<h3 id="02开启一个-fabric-network">0.2.开启一个 Fabric Network</h3>

<p>使用以下命令开启一个Fabric Network</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># OPTIONAL</span>
<span class="nb">export </span><span class="nv">HLF_VERSION</span><span class="o">=</span>1.4.0
docker pull hyperledger/fabric-peer:<span class="k">${</span><span class="nv">HLF_VERSION</span><span class="k">}</span>
docker pull hyperledger/fabric-orderer:<span class="k">${</span><span class="nv">HLF_VERSION</span><span class="k">}</span>
docker pull hyperledger/fabric-ca:<span class="k">${</span><span class="nv">HLF_VERSION</span><span class="k">}</span>
docker pull hyperledger/fabric-ccenv:<span class="k">${</span><span class="nv">HLF_VERSION</span><span class="k">}</span>
git clone https://github.com/hyperledger/fabric-sdk-py.git
<span class="nb">cd </span>fabric-sdk-py 
docker-compose <span class="nt">-f</span> <span class="nb">test</span>/fixtures/docker-compose-2orgs-4peers-tls.yaml up
</code></pre></div></div>

<p>然后你将有一个有3个组织的Fabric网络，4个普通节点和1个排序节点，网络拓扑结构如下：</p>
<ul>
  <li>org1.example.com
    <ul>
      <li>peer0.org1.example.com</li>
      <li>peer1.org1.example.com</li>
    </ul>
  </li>
  <li>org2.example.com
    <ul>
      <li>peer0.org2.example.com</li>
      <li>peer1.org2.example.com</li>
    </ul>
  </li>
  <li>orderer.example.com
    <ul>
      <li>orderer.example.com</li>
    </ul>
  </li>
</ul>

<p>*注意： <code class="language-plaintext highlighter-rouge">configtxgen</code>需要在<code class="language-plaintext highlighter-rouge">PATH</code>路径下</p>

<h3 id="03-建立连接配置文件">0.3. 建立连接配置文件</h3>

<p>一个网络连接配置文件通过提供跟网络交互所需要的所有信息帮助SDK连接到Fabric网络中，其中包括：</p>
<ul>
  <li>peer, orderer, ca的服务端点</li>
  <li>客户端身份的认证证书
一个例子是 <a href="https://github.com/hyperledger/fabric-sdk-py/blob/master/test/fixtures/network.json">network.json</a></li>
</ul>

<h2 id="1-加载连接描述connection-profile">1. 加载连接描述(Connection Profile)</h2>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">hfc.fabric</span> <span class="kn">import</span> <span class="n">Client</span>

<span class="n">cli</span> <span class="o">=</span> <span class="n">Client</span><span class="p">(</span><span class="n">net_profile</span><span class="o">=</span><span class="s">"test/fixtures/network.json"</span><span class="p">)</span>

<span class="k">print</span><span class="p">(</span><span class="n">cli</span><span class="p">.</span><span class="n">organizations</span><span class="p">)</span>  <span class="c1"># orgs in the network
</span><span class="k">print</span><span class="p">(</span><span class="n">cli</span><span class="p">.</span><span class="n">peers</span><span class="p">)</span>  <span class="c1"># peers in the network
</span><span class="k">print</span><span class="p">(</span><span class="n">cli</span><span class="p">.</span><span class="n">orderers</span><span class="p">)</span>  <span class="c1"># orderers in the network
</span><span class="k">print</span><span class="p">(</span><span class="n">cli</span><span class="p">.</span><span class="n">CAs</span><span class="p">)</span>  <span class="c1"># ca nodes in the network, TODO
</span></code></pre></div></div>

<h2 id="2-在fabric网络中channel的操作">2. 在Fabric网络中Channel的操作</h2>

<h3 id="21-建立并加入新通道">2.1 建立并加入新通道</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">asyncio</span>
<span class="kn">from</span> <span class="nn">hfc.fabric</span> <span class="kn">import</span> <span class="n">Client</span>

<span class="n">loop</span> <span class="o">=</span> <span class="n">asyncio</span><span class="p">.</span><span class="n">get_event_loop</span><span class="p">()</span>

<span class="n">cli</span> <span class="o">=</span> <span class="n">Client</span><span class="p">(</span><span class="n">net_profile</span><span class="o">=</span><span class="s">"test/fixtures/network.json"</span><span class="p">)</span>
<span class="n">org1_admin</span> <span class="o">=</span> <span class="n">cli</span><span class="p">.</span><span class="n">get_user</span><span class="p">(</span><span class="n">org_name</span><span class="o">=</span><span class="s">'org1.example.com'</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="s">'Admin'</span><span class="p">)</span>

<span class="c1"># Create a New Channel, the response should be true if succeed
</span><span class="n">response</span> <span class="o">=</span> <span class="n">loop</span><span class="p">.</span><span class="n">run_until_complete</span><span class="p">(</span><span class="n">cli</span><span class="p">.</span><span class="n">channel_create</span><span class="p">(</span>
            <span class="n">orderer</span><span class="o">=</span><span class="s">'orderer.example.com'</span><span class="p">,</span>
            <span class="n">channel_name</span><span class="o">=</span><span class="s">'businesschannel'</span><span class="p">,</span>
            <span class="n">requestor</span><span class="o">=</span><span class="n">org1_admin</span><span class="p">,</span>
            <span class="n">config_yaml</span><span class="o">=</span><span class="s">'test/fixtures/e2e_cli/'</span><span class="p">,</span>
            <span class="n">channel_profile</span><span class="o">=</span><span class="s">'TwoOrgsChannel'</span>
            <span class="p">))</span>
<span class="k">print</span><span class="p">(</span><span class="n">response</span> <span class="o">==</span> <span class="bp">True</span><span class="p">)</span>

<span class="c1"># Join Peers into Channel, the response should be true if succeed
</span><span class="n">orderer_admin</span> <span class="o">=</span> <span class="n">cli</span><span class="p">.</span><span class="n">get_user</span><span class="p">(</span><span class="n">org_name</span><span class="o">=</span><span class="s">'orderer.example.com'</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="s">'Admin'</span><span class="p">)</span>
<span class="n">responses</span> <span class="o">=</span> <span class="n">loop</span><span class="p">.</span><span class="n">run_until_complete</span><span class="p">(</span><span class="n">cli</span><span class="p">.</span><span class="n">channel_join</span><span class="p">(</span>
               <span class="n">requestor</span><span class="o">=</span><span class="n">org1_admin</span><span class="p">,</span>
               <span class="n">channel_name</span><span class="o">=</span><span class="s">'businesschannel'</span><span class="p">,</span>
               <span class="n">peers</span><span class="o">=</span><span class="p">[</span><span class="s">'peer0.org1.example.com'</span><span class="p">,</span>
                      <span class="s">'peer1.org1.example.com'</span><span class="p">],</span>

               <span class="n">orderer</span><span class="o">=</span><span class="s">'orderer.example.com'</span><span class="p">,</span>
               <span class="n">orderer_admin</span><span class="o">=</span><span class="n">orderer_admin</span>
               <span class="p">))</span>
<span class="k">print</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">responses</span><span class="p">)</span> <span class="o">==</span> <span class="mi">2</span><span class="p">)</span>


<span class="c1"># Join Peers from a different MSP into Channel
</span><span class="n">org2_admin</span> <span class="o">=</span> <span class="n">cli</span><span class="p">.</span><span class="n">get_user</span><span class="p">(</span><span class="n">org_name</span><span class="o">=</span><span class="s">'org2.example.com'</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="s">'Admin'</span><span class="p">)</span>

<span class="c1"># For operations on peers from org2.example.com, org2_admin is required as requestor
</span><span class="n">responses</span> <span class="o">=</span> <span class="n">loop</span><span class="p">.</span><span class="n">run_until_complete</span><span class="p">(</span><span class="n">cli</span><span class="p">.</span><span class="n">channel_join</span><span class="p">(</span>
               <span class="n">requestor</span><span class="o">=</span><span class="n">org2_admin</span><span class="p">,</span>
               <span class="n">channel_name</span><span class="o">=</span><span class="s">'businesschannel'</span><span class="p">,</span>
               <span class="n">peers</span><span class="o">=</span><span class="p">[</span><span class="s">'peer0.org2.example.com'</span><span class="p">,</span>
                      <span class="s">'peer1.org2.example.com'</span><span class="p">],</span>
               <span class="n">orderer</span><span class="o">=</span><span class="s">'orderer.example.com'</span><span class="p">,</span>
               <span class="n">orderer_admin</span><span class="o">=</span><span class="n">orderer_admin</span>
               <span class="p">))</span>
<span class="k">print</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">responses</span><span class="p">)</span> <span class="o">==</span> <span class="mi">2</span><span class="p">)</span>
</code></pre></div></div>

<h3 id="22-更新通道信息">2.2 更新通道信息</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">asyncio</span>
<span class="kn">from</span> <span class="nn">hfc.fabric</span> <span class="kn">import</span> <span class="n">Client</span>

<span class="n">loop</span> <span class="o">=</span> <span class="n">asyncio</span><span class="p">.</span><span class="n">get_event_loop</span><span class="p">()</span>

<span class="n">cli</span> <span class="o">=</span> <span class="n">Client</span><span class="p">(</span><span class="n">net_profile</span><span class="o">=</span><span class="s">"test/fixtures/network.json"</span><span class="p">)</span>
<span class="n">org1_admin</span> <span class="o">=</span> <span class="n">cli</span><span class="p">.</span><span class="n">get_user</span><span class="p">(</span><span class="n">org_name</span><span class="o">=</span><span class="s">'org1.example.com'</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="s">'Admin'</span><span class="p">)</span>

<span class="n">config_tx_file</span> <span class="o">=</span> <span class="s">'./configtx.yaml'</span>

<span class="n">orderer_admin</span> <span class="o">=</span> <span class="n">cli</span><span class="p">.</span><span class="n">get_user</span><span class="p">(</span><span class="n">org_name</span><span class="o">=</span><span class="s">'orderer.example.com'</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="s">'Admin'</span><span class="p">)</span>
<span class="n">loop</span><span class="p">.</span><span class="n">run_until_complete</span><span class="p">(</span><span class="n">cli</span><span class="p">.</span><span class="n">channel_update</span><span class="p">(</span>
        <span class="n">orderer</span><span class="o">=</span><span class="s">'orderer.example.com'</span><span class="p">,</span>
        <span class="n">channel_name</span><span class="o">=</span><span class="s">'businesschannel'</span><span class="p">,</span>
        <span class="n">requestor</span><span class="o">=</span><span class="n">orderer_admin</span><span class="p">,</span>
        <span class="n">config_tx</span><span class="o">=</span><span class="n">config_tx_file</span><span class="p">))</span>
</code></pre></div></div>

<h2 id="3-在fabric网络中进行chaincode相关操作">3. 在Fabric网络中进行Chaincode相关操作</h2>

<ul>
  <li>使用sdk进行install, instantiate和invoke chaincode的操作</li>
</ul>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">asyncio</span>
<span class="kn">from</span> <span class="nn">hfc.fabric</span> <span class="kn">import</span> <span class="n">Client</span>

<span class="n">loop</span> <span class="o">=</span> <span class="n">asyncio</span><span class="p">.</span><span class="n">get_event_loop</span><span class="p">()</span>

<span class="n">cli</span> <span class="o">=</span> <span class="n">Client</span><span class="p">(</span><span class="n">net_profile</span><span class="o">=</span><span class="s">"test/fixtures/network.json"</span><span class="p">)</span>
<span class="n">org1_admin</span> <span class="o">=</span> <span class="n">cli</span><span class="p">.</span><span class="n">get_user</span><span class="p">(</span><span class="s">'org1.example.com'</span><span class="p">,</span> <span class="s">'Admin'</span><span class="p">)</span>

<span class="c1"># Make the client know there is a channel in the network
</span><span class="n">cli</span><span class="p">.</span><span class="n">new_channel</span><span class="p">(</span><span class="s">'businesschannel'</span><span class="p">)</span>

<span class="c1"># Install Example Chaincode to Peers
# GOPATH setting is only needed to use the example chaincode inside sdk
</span><span class="kn">import</span> <span class="nn">os</span>
<span class="n">gopath_bak</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">environ</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">'GOPATH'</span><span class="p">,</span> <span class="s">''</span><span class="p">)</span>
<span class="n">gopath</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">normpath</span><span class="p">(</span><span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">join</span><span class="p">(</span>
                      <span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">dirname</span><span class="p">(</span><span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">realpath</span><span class="p">(</span><span class="s">'__file__'</span><span class="p">)),</span>
                      <span class="s">'test/fixtures/chaincode'</span>
                     <span class="p">))</span>
<span class="n">os</span><span class="p">.</span><span class="n">environ</span><span class="p">[</span><span class="s">'GOPATH'</span><span class="p">]</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">abspath</span><span class="p">(</span><span class="n">gopath</span><span class="p">)</span>

<span class="c1"># The response should be true if succeed
</span><span class="n">responses</span> <span class="o">=</span> <span class="n">loop</span><span class="p">.</span><span class="n">run_until_complete</span><span class="p">(</span><span class="n">cli</span><span class="p">.</span><span class="n">chaincode_install</span><span class="p">(</span>
               <span class="n">requestor</span><span class="o">=</span><span class="n">org1_admin</span><span class="p">,</span>
               <span class="n">peers</span><span class="o">=</span><span class="p">[</span><span class="s">'peer0.org1.example.com'</span><span class="p">,</span>
                      <span class="s">'peer1.org1.example.com'</span><span class="p">],</span>
               <span class="n">cc_path</span><span class="o">=</span><span class="s">'github.com/example_cc'</span><span class="p">,</span>
               <span class="n">cc_name</span><span class="o">=</span><span class="s">'example_cc'</span><span class="p">,</span>
               <span class="n">cc_version</span><span class="o">=</span><span class="s">'v1.0'</span>
               <span class="p">))</span>

<span class="c1"># Instantiate Chaincode in Channel, the response should be true if succeed
</span><span class="n">args</span> <span class="o">=</span> <span class="p">[</span><span class="s">'a'</span><span class="p">,</span> <span class="s">'200'</span><span class="p">,</span> <span class="s">'b'</span><span class="p">,</span> <span class="s">'300'</span><span class="p">]</span>

<span class="c1"># policy, see https://hyperledger-fabric.readthedocs.io/en/release-1.4/endorsement-policies.html
</span><span class="n">policy</span> <span class="o">=</span> <span class="p">{</span>
    <span class="s">'identities'</span><span class="p">:</span> <span class="p">[</span>
        <span class="p">{</span><span class="s">'role'</span><span class="p">:</span> <span class="p">{</span><span class="s">'name'</span><span class="p">:</span> <span class="s">'member'</span><span class="p">,</span> <span class="s">'mspId'</span><span class="p">:</span> <span class="s">'Org1MSP'</span><span class="p">}},</span>
    <span class="p">],</span>
    <span class="s">'policy'</span><span class="p">:</span> <span class="p">{</span>
        <span class="s">'1-of'</span><span class="p">:</span> <span class="p">[</span>
            <span class="p">{</span><span class="s">'signed-by'</span><span class="p">:</span> <span class="mi">0</span><span class="p">},</span>
        <span class="p">]</span>
    <span class="p">}</span>
<span class="p">}</span>
<span class="n">response</span> <span class="o">=</span> <span class="n">loop</span><span class="p">.</span><span class="n">run_until_complete</span><span class="p">(</span><span class="n">cli</span><span class="p">.</span><span class="n">chaincode_instantiate</span><span class="p">(</span>
               <span class="n">requestor</span><span class="o">=</span><span class="n">org1_admin</span><span class="p">,</span>
               <span class="n">channel_name</span><span class="o">=</span><span class="s">'businesschannel'</span><span class="p">,</span>
               <span class="n">peers</span><span class="o">=</span><span class="p">[</span><span class="s">'peer0.org1.example.com'</span><span class="p">],</span>
               <span class="n">args</span><span class="o">=</span><span class="n">args</span><span class="p">,</span>
               <span class="n">cc_name</span><span class="o">=</span><span class="s">'example_cc'</span><span class="p">,</span>
               <span class="n">cc_version</span><span class="o">=</span><span class="s">'v1.0'</span><span class="p">,</span>
               <span class="n">cc_endorsement_policy</span><span class="o">=</span><span class="n">policy</span><span class="p">,</span> <span class="c1"># optional, but recommended
</span>               <span class="n">collections_config</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="c1"># optional, for private data policy
</span>               <span class="n">transient_map</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="c1"># optional, for private data
</span>               <span class="n">wait_for_event</span><span class="o">=</span><span class="bp">True</span> <span class="c1"># optional, for being sure chaincode is instantiated
</span>               <span class="p">))</span>

<span class="c1"># Invoke a chaincode
</span><span class="n">args</span> <span class="o">=</span> <span class="p">[</span><span class="s">'a'</span><span class="p">,</span> <span class="s">'b'</span><span class="p">,</span> <span class="s">'100'</span><span class="p">]</span>
<span class="c1"># The response should be true if succeed
</span><span class="n">response</span> <span class="o">=</span> <span class="n">loop</span><span class="p">.</span><span class="n">run_until_complete</span><span class="p">(</span><span class="n">cli</span><span class="p">.</span><span class="n">chaincode_invoke</span><span class="p">(</span>
               <span class="n">requestor</span><span class="o">=</span><span class="n">org1_admin</span><span class="p">,</span>
               <span class="n">channel_name</span><span class="o">=</span><span class="s">'businesschannel'</span><span class="p">,</span>
               <span class="n">peers</span><span class="o">=</span><span class="p">[</span><span class="s">'peer0.org1.example.com'</span><span class="p">],</span>
               <span class="n">args</span><span class="o">=</span><span class="n">args</span><span class="p">,</span>
               <span class="n">cc_name</span><span class="o">=</span><span class="s">'example_cc'</span><span class="p">,</span>
               <span class="n">transient_map</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="c1"># optional, for private data
</span>               <span class="n">wait_for_event</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="c1"># for being sure chaincode invocation has been commited in the ledger, default is on tx event
</span>               <span class="c1">#cc_pattern='^invoked*' # if you want to wait for chaincode event and you have a `stub.SetEvent("invoked", value)` in your chaincode
</span>               <span class="p">))</span>

<span class="c1"># Query a chaincode
</span><span class="n">args</span> <span class="o">=</span> <span class="p">[</span><span class="s">'b'</span><span class="p">]</span>
<span class="c1"># The response should be true if succeed
</span><span class="n">response</span> <span class="o">=</span> <span class="n">loop</span><span class="p">.</span><span class="n">run_until_complete</span><span class="p">(</span><span class="n">cli</span><span class="p">.</span><span class="n">chaincode_query</span><span class="p">(</span>
               <span class="n">requestor</span><span class="o">=</span><span class="n">org1_admin</span><span class="p">,</span>
               <span class="n">channel_name</span><span class="o">=</span><span class="s">'businesschannel'</span><span class="p">,</span>
               <span class="n">peers</span><span class="o">=</span><span class="p">[</span><span class="s">'peer0.org1.example.com'</span><span class="p">],</span>
               <span class="n">args</span><span class="o">=</span><span class="n">args</span><span class="p">,</span>
               <span class="n">cc_name</span><span class="o">=</span><span class="s">'example_cc'</span>
               <span class="p">))</span>

<span class="c1"># Upgrade a chaincode
# policy, see https://hyperledger-fabric.readthedocs.io/en/release-1.4/endorsement-policies.html
</span><span class="n">policy</span> <span class="o">=</span> <span class="p">{</span>
    <span class="s">'identities'</span><span class="p">:</span> <span class="p">[</span>
        <span class="p">{</span><span class="s">'role'</span><span class="p">:</span> <span class="p">{</span><span class="s">'name'</span><span class="p">:</span> <span class="s">'member'</span><span class="p">,</span> <span class="s">'mspId'</span><span class="p">:</span> <span class="s">'Org1MSP'</span><span class="p">}},</span>
        <span class="p">{</span><span class="s">'role'</span><span class="p">:</span> <span class="p">{</span><span class="s">'name'</span><span class="p">:</span> <span class="s">'admin'</span><span class="p">,</span> <span class="s">'mspId'</span><span class="p">:</span> <span class="s">'Org1MSP'</span><span class="p">}},</span>
    <span class="p">],</span>
    <span class="s">'policy'</span><span class="p">:</span> <span class="p">{</span>
        <span class="s">'1-of'</span><span class="p">:</span> <span class="p">[</span>
            <span class="p">{</span><span class="s">'signed-by'</span><span class="p">:</span> <span class="mi">0</span><span class="p">},</span> <span class="p">{</span><span class="s">'signed-by'</span><span class="p">:</span> <span class="mi">1</span><span class="p">},</span>
        <span class="p">]</span>
    <span class="p">}</span>
<span class="p">}</span>
<span class="n">response</span> <span class="o">=</span> <span class="n">loop</span><span class="p">.</span><span class="n">run_until_complete</span><span class="p">(</span><span class="n">cli</span><span class="p">.</span><span class="n">chaincode_upgrade</span><span class="p">(</span>
               <span class="n">requestor</span><span class="o">=</span><span class="n">org1_admin</span><span class="p">,</span>
               <span class="n">channel_name</span><span class="o">=</span><span class="s">'businesschannel'</span><span class="p">,</span>
               <span class="n">peers</span><span class="o">=</span><span class="p">[</span><span class="s">'peer0.org1.example.com'</span><span class="p">],</span>
               <span class="n">args</span><span class="o">=</span><span class="n">args</span><span class="p">,</span>
               <span class="n">cc_name</span><span class="o">=</span><span class="s">'example_cc'</span><span class="p">,</span>
               <span class="n">cc_version</span><span class="o">=</span><span class="s">'v1.0'</span><span class="p">,</span>
               <span class="n">cc_endorsement_policy</span><span class="o">=</span><span class="n">policy</span><span class="p">,</span> <span class="c1"># optional, but recommended
</span>               <span class="n">collections_config</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="c1"># optional, for private data policy
</span>               <span class="n">transient_map</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="c1"># optional, for private data
</span>               <span class="n">wait_for_event</span><span class="o">=</span><span class="bp">True</span> <span class="c1"># optional, for being sure chaincode is upgraded
</span>               <span class="p">))</span>    
</code></pre></div></div>

<h2 id="4-查询信息">4. 查询信息</h2>

<h3 id="41-基本操作">4.1 基本操作</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">asyncio</span>
<span class="kn">from</span> <span class="nn">hfc.fabric</span> <span class="kn">import</span> <span class="n">Client</span>

<span class="n">loop</span> <span class="o">=</span> <span class="n">asyncio</span><span class="p">.</span><span class="n">get_event_loop</span><span class="p">()</span>
<span class="n">cli</span> <span class="o">=</span> <span class="n">Client</span><span class="p">(</span><span class="n">net_profile</span><span class="o">=</span><span class="s">"test/fixtures/network.json"</span><span class="p">)</span>
<span class="n">org1_admin</span> <span class="o">=</span> <span class="n">cli</span><span class="p">.</span><span class="n">get_user</span><span class="p">(</span><span class="s">'org1.example.com'</span><span class="p">,</span> <span class="s">'Admin'</span><span class="p">)</span>

<span class="c1"># Query Peer installed chaincodes, make sure the chaincode is installed
</span><span class="n">response</span> <span class="o">=</span> <span class="n">loop</span><span class="p">.</span><span class="n">run_until_complete</span><span class="p">(</span><span class="n">cli</span><span class="p">.</span><span class="n">query_installed_chaincodes</span><span class="p">(</span>
               <span class="n">requestor</span><span class="o">=</span><span class="n">org1_admin</span><span class="p">,</span>
               <span class="n">peers</span><span class="o">=</span><span class="p">[</span><span class="s">'peer0.org1.example.com'</span><span class="p">],</span>
               <span class="n">decode</span><span class="o">=</span><span class="bp">True</span>
               <span class="p">))</span>

<span class="s">"""
# An example response:

chaincodes {
  name: "example_cc"
  version: "1.0"
  path: "github.com/example_cc"
}
"""</span>

<span class="c1"># Query Peer Joined channel
</span><span class="n">response</span> <span class="o">=</span> <span class="n">loop</span><span class="p">.</span><span class="n">run_until_complete</span><span class="p">(</span><span class="n">cli</span><span class="p">.</span><span class="n">query_channels</span><span class="p">(</span>
               <span class="n">requestor</span><span class="o">=</span><span class="n">org1_admin</span><span class="p">,</span>
               <span class="n">peers</span><span class="o">=</span><span class="p">[</span><span class="s">'peer0.org1.example.com'</span><span class="p">],</span>
               <span class="n">decode</span><span class="o">=</span><span class="bp">True</span>
               <span class="p">))</span>

<span class="s">"""
# An example response:

channels {
  channel_id: "businesschannel"
}
"""</span>

<span class="c1"># Query Channel Info
</span><span class="n">response</span> <span class="o">=</span> <span class="n">loop</span><span class="p">.</span><span class="n">run_until_complete</span><span class="p">(</span><span class="n">cli</span><span class="p">.</span><span class="n">query_info</span><span class="p">(</span>
               <span class="n">requestor</span><span class="o">=</span><span class="n">org1_admin</span><span class="p">,</span>
               <span class="n">channel_name</span><span class="o">=</span><span class="s">'businesschannel'</span><span class="p">,</span>
               <span class="n">peers</span><span class="o">=</span><span class="p">[</span><span class="s">'peer0.org1.example.com'</span><span class="p">],</span>
               <span class="n">decode</span><span class="o">=</span><span class="bp">True</span>
               <span class="p">))</span>

<span class="c1"># Query Block by tx id
# example txid of instantiated chaincode transaction
</span><span class="n">response</span> <span class="o">=</span> <span class="n">loop</span><span class="p">.</span><span class="n">run_until_complete</span><span class="p">(</span><span class="n">cli</span><span class="p">.</span><span class="n">query_block_by_txid</span><span class="p">(</span>
               <span class="n">requestor</span><span class="o">=</span><span class="n">org1_admin</span><span class="p">,</span>
               <span class="n">channel_name</span><span class="o">=</span><span class="s">'businesschannel'</span><span class="p">,</span>
               <span class="n">peers</span><span class="o">=</span><span class="p">[</span><span class="s">'peer0.org1.example.com'</span><span class="p">],</span>
               <span class="n">tx_id</span><span class="o">=</span><span class="n">cli</span><span class="p">.</span><span class="n">txid_for_test</span><span class="p">,</span>
               <span class="n">decode</span><span class="o">=</span><span class="bp">True</span>
               <span class="p">))</span>
</code></pre></div></div>

<h3 id="42-通过哈希值查询区块">4.2 通过哈希值查询区块</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">asyncio</span>
<span class="kn">from</span> <span class="nn">hfc.fabric</span> <span class="kn">import</span> <span class="n">Client</span>

<span class="n">loop</span> <span class="o">=</span> <span class="n">asyncio</span><span class="p">.</span><span class="n">get_event_loop</span><span class="p">()</span>
<span class="n">cli</span> <span class="o">=</span> <span class="n">Client</span><span class="p">(</span><span class="n">net_profile</span><span class="o">=</span><span class="s">"test/fixtures/network.json"</span><span class="p">)</span>
<span class="n">org1_admin</span> <span class="o">=</span> <span class="n">cli</span><span class="p">.</span><span class="n">get_user</span><span class="p">(</span><span class="s">'org1.example.com'</span><span class="p">,</span> <span class="s">'Admin'</span><span class="p">)</span>

<span class="c1"># first get the hash by calling 'query_info'
</span><span class="n">response</span> <span class="o">=</span> <span class="n">loop</span><span class="p">.</span><span class="n">run_until_complete</span><span class="p">(</span><span class="n">cli</span><span class="p">.</span><span class="n">query_info</span><span class="p">(</span>
               <span class="n">requestor</span><span class="o">=</span><span class="n">org1_admin</span><span class="p">,</span>
               <span class="n">channel_name</span><span class="o">=</span><span class="s">'businesschannel'</span><span class="p">,</span>
               <span class="n">peers</span><span class="o">=</span><span class="p">[</span><span class="s">'peer0.org1.example.com'</span><span class="p">],</span>
               <span class="n">decode</span><span class="o">=</span><span class="bp">True</span>
               <span class="p">))</span>

<span class="n">test_hash</span> <span class="o">=</span> <span class="n">response</span><span class="p">.</span><span class="n">currentBlockHash</span>

<span class="n">response</span> <span class="o">=</span> <span class="n">loop</span><span class="p">.</span><span class="n">run_until_complete</span><span class="p">(</span><span class="n">cli</span><span class="p">.</span><span class="n">query_block_by_hash</span><span class="p">(</span>
               <span class="n">requestor</span><span class="o">=</span><span class="n">org1_admin</span><span class="p">,</span>
               <span class="n">channel_name</span><span class="o">=</span><span class="s">'businesschannel'</span><span class="p">,</span>
               <span class="n">peers</span><span class="o">=</span><span class="p">[</span><span class="s">'peer0.org1.example.com'</span><span class="p">],</span>
               <span class="n">block_hash</span><span class="o">=</span><span class="n">test_hash</span><span class="p">,</span>
               <span class="n">decode</span><span class="o">=</span><span class="bp">True</span>
               <span class="p">))</span>
</code></pre></div></div>

<h3 id="43-查询区块交易和启动的链码">4.3 查询区块，交易和启动的链码</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">asyncio</span>
<span class="kn">from</span> <span class="nn">hfc.fabric</span> <span class="kn">import</span> <span class="n">Client</span>

<span class="n">loop</span> <span class="o">=</span> <span class="n">asyncio</span><span class="p">.</span><span class="n">get_event_loop</span><span class="p">()</span>
<span class="n">cli</span> <span class="o">=</span> <span class="n">Client</span><span class="p">(</span><span class="n">net_profile</span><span class="o">=</span><span class="s">"test/fixtures/network.json"</span><span class="p">)</span>
<span class="n">org1_admin</span> <span class="o">=</span> <span class="n">cli</span><span class="p">.</span><span class="n">get_user</span><span class="p">(</span><span class="s">'org1.example.com'</span><span class="p">,</span> <span class="s">'Admin'</span><span class="p">)</span>

<span class="c1"># Query Block by block number
</span><span class="n">response</span> <span class="o">=</span> <span class="n">loop</span><span class="p">.</span><span class="n">run_until_complete</span><span class="p">(</span><span class="n">cli</span><span class="p">.</span><span class="n">query_block</span><span class="p">(</span>
               <span class="n">requestor</span><span class="o">=</span><span class="n">org1_admin</span><span class="p">,</span>
               <span class="n">channel_name</span><span class="o">=</span><span class="s">'businesschannel'</span><span class="p">,</span>
               <span class="n">peers</span><span class="o">=</span><span class="p">[</span><span class="s">'peer0.org1.example.com'</span><span class="p">],</span>
               <span class="n">block_number</span><span class="o">=</span><span class="s">'1'</span><span class="p">,</span>
               <span class="n">decode</span><span class="o">=</span><span class="bp">True</span>
               <span class="p">))</span>

<span class="c1"># Query Transaction by tx id
# example txid of instantiated chaincode transaction
</span><span class="n">response</span> <span class="o">=</span> <span class="n">loop</span><span class="p">.</span><span class="n">run_until_complete</span><span class="p">(</span><span class="n">cli</span><span class="p">.</span><span class="n">query_transaction</span><span class="p">(</span>
               <span class="n">requestor</span><span class="o">=</span><span class="n">org1_admin</span><span class="p">,</span>
               <span class="n">channel_name</span><span class="o">=</span><span class="s">'businesschannel'</span><span class="p">,</span>
               <span class="n">peers</span><span class="o">=</span><span class="p">[</span><span class="s">'peer0.org1.example.com'</span><span class="p">],</span>
               <span class="n">tx_id</span><span class="o">=</span><span class="n">cli</span><span class="p">.</span><span class="n">txid_for_test</span><span class="p">,</span>
               <span class="n">decode</span><span class="o">=</span><span class="bp">True</span>
               <span class="p">))</span>

<span class="c1"># Query Instantiated Chaincodes
</span><span class="n">response</span> <span class="o">=</span> <span class="n">loop</span><span class="p">.</span><span class="n">run_until_complete</span><span class="p">(</span><span class="n">cli</span><span class="p">.</span><span class="n">query_instantiated_chaincodes</span><span class="p">(</span>
               <span class="n">requestor</span><span class="o">=</span><span class="n">org1_admin</span><span class="p">,</span>
               <span class="n">channel_name</span><span class="o">=</span><span class="s">'businesschannel'</span><span class="p">,</span>
               <span class="n">peers</span><span class="o">=</span><span class="p">[</span><span class="s">'peer0.org1.example.com'</span><span class="p">],</span>
               <span class="n">decode</span><span class="o">=</span><span class="bp">True</span>
               <span class="p">))</span>
</code></pre></div></div>

<h3 id="44-获得通道信息">4.4 获得通道信息</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">asyncio</span>
<span class="kn">from</span> <span class="nn">hfc.fabric</span> <span class="kn">import</span> <span class="n">Client</span>

<span class="n">loop</span> <span class="o">=</span> <span class="n">asyncio</span><span class="p">.</span><span class="n">get_event_loop</span><span class="p">()</span>
<span class="n">cli</span> <span class="o">=</span> <span class="n">Client</span><span class="p">(</span><span class="n">net_profile</span><span class="o">=</span><span class="s">"test/fixtures/network.json"</span><span class="p">)</span>
<span class="n">org1_admin</span> <span class="o">=</span> <span class="n">cli</span><span class="p">.</span><span class="n">get_user</span><span class="p">(</span><span class="s">'org1.example.com'</span><span class="p">,</span> <span class="s">'Admin'</span><span class="p">)</span>

<span class="c1"># Get channel config
</span><span class="n">response</span> <span class="o">=</span> <span class="n">loop</span><span class="p">.</span><span class="n">run_until_complete</span><span class="p">(</span><span class="n">cli</span><span class="p">.</span><span class="n">get_channel_config</span><span class="p">(</span>
               <span class="n">requestor</span><span class="o">=</span><span class="n">org1_admin</span><span class="p">,</span>
               <span class="n">channel_name</span><span class="o">=</span><span class="s">'businesschannel'</span><span class="p">,</span>
               <span class="n">peers</span><span class="o">=</span><span class="p">[</span><span class="s">'peer0.org1.example.com'</span><span class="p">],</span>
               <span class="n">decode</span><span class="o">=</span><span class="bp">True</span>
               <span class="p">))</span>
</code></pre></div></div>

<h3 id="45-使用通道发现-channel-discovery">4.5 使用通道发现 (channel discovery)</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">asyncio</span>
<span class="kn">from</span> <span class="nn">hfc.fabric</span> <span class="kn">import</span> <span class="n">Client</span>

<span class="n">loop</span> <span class="o">=</span> <span class="n">asyncio</span><span class="p">.</span><span class="n">get_event_loop</span><span class="p">()</span>
<span class="n">cli</span> <span class="o">=</span> <span class="n">Client</span><span class="p">(</span><span class="n">net_profile</span><span class="o">=</span><span class="s">"test/fixtures/network.json"</span><span class="p">)</span>
<span class="n">org1_admin</span> <span class="o">=</span> <span class="n">cli</span><span class="p">.</span><span class="n">get_user</span><span class="p">(</span><span class="s">'org1.example.com'</span><span class="p">,</span> <span class="s">'Admin'</span><span class="p">)</span>

<span class="c1"># Get config from local channel discovery
</span><span class="n">response</span> <span class="o">=</span> <span class="n">loop</span><span class="p">.</span><span class="n">run_until_complete</span><span class="p">(</span><span class="n">cli</span><span class="p">.</span><span class="n">query_peers</span><span class="p">(</span>
               <span class="n">requestor</span><span class="o">=</span><span class="n">org1_admin</span><span class="p">,</span>
               <span class="n">peer</span><span class="o">=</span><span class="s">'peer0.org1.example.com'</span><span class="p">,</span>
               <span class="n">channel</span><span class="o">=</span><span class="s">'businesschannel'</span><span class="p">,</span>
               <span class="n">local</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span>
               <span class="n">decode</span><span class="o">=</span><span class="bp">True</span>
               <span class="p">))</span>

<span class="c1"># Get config from channel discovery over the network
</span><span class="n">response</span> <span class="o">=</span> <span class="n">loop</span><span class="p">.</span><span class="n">run_until_complete</span><span class="p">(</span><span class="n">cli</span><span class="p">.</span><span class="n">query_peers</span><span class="p">(</span>
               <span class="n">requestor</span><span class="o">=</span><span class="n">org1_admin</span><span class="p">,</span>
               <span class="n">peer</span><span class="o">=</span><span class="s">'peer0.org1.example.com'</span><span class="p">,</span>
               <span class="n">channel</span><span class="o">=</span><span class="s">'businesschannel'</span><span class="p">,</span>
               <span class="n">local</span><span class="o">=</span><span class="bp">False</span><span class="p">,</span>
               <span class="n">decode</span><span class="o">=</span><span class="bp">True</span>
               <span class="p">))</span>
</code></pre></div></div>]]></content><author><name></name></author><category term="fabric" /><category term="fabric" /><category term="tutorial" /><summary type="html"><![CDATA[本文基于fabric-sdk-py官方文档翻译，如有更多疑问，请移步至Rocketchat #fabric-sdk-py，谢谢！]]></summary></entry><entry><title type="html">Difference between Endorsement Policy and MSP in Fabric</title><link href="https://blog.dex.moe/fabric/tutorial/2018/11/10/difference-betweeen-endorsement-policy-and-msp.html" rel="alternate" type="text/html" title="Difference between Endorsement Policy and MSP in Fabric" /><published>2018-11-10T16:02:08+00:00</published><updated>2018-11-10T16:02:08+00:00</updated><id>https://blog.dex.moe/fabric/tutorial/2018/11/10/difference-betweeen-endorsement-policy-and-msp</id><content type="html" xml:base="https://blog.dex.moe/fabric/tutorial/2018/11/10/difference-betweeen-endorsement-policy-and-msp.html"><![CDATA[<h2 id="background">Background</h2>

<p>Being one of the maintainers of fabric-sdk-py for a while now, I have somewhat deeper understanding of the fabric framework and blockchain technology in general. However, some concepts in fabric seems to be overwhelmingly hard to understand or the official documentation seems too rebundant for beginners as one of my colleges in Beihang University reported. So I decide to write some short tutorials to demystify some frequently used features with examples in Hyperledger Fabric.</p>

<h2 id="what-is-endorsement-policy">What is Endorsement Policy?</h2>

<h4 id="functionality-of-endorsement-policy">Functionality of Endorsement Policy</h4>

<p>Endorsement Policy sets the rules about the number of peers/orgs which must recach agreement on the execution of a chaincode given a set of inputs.</p>

<h2 id="what-is-msp-membership-service-provider">What is MSP (Membership Service Provider)?</h2>

<h2 id="how-the-two-related">How the two related?</h2>]]></content><author><name></name></author><category term="fabric" /><category term="tutorial" /><category term="fabric" /><summary type="html"><![CDATA[Background]]></summary></entry><entry><title type="html">A Hands-on Tutorial on Building Your First DAPP with CITA (Nervos)</title><link href="https://blog.dex.moe/tutorial/2018/09/04/tutorial-on-cita.html" rel="alternate" type="text/html" title="A Hands-on Tutorial on Building Your First DAPP with CITA (Nervos)" /><published>2018-09-04T00:14:39+00:00</published><updated>2018-09-04T00:14:39+00:00</updated><id>https://blog.dex.moe/tutorial/2018/09/04/tutorial-on-cita</id><content type="html" xml:base="https://blog.dex.moe/tutorial/2018/09/04/tutorial-on-cita.html"><![CDATA[<p>Although the documentation of cita is already in details, there are some tricks that I would like to share.</p>

<h2 id="prerequisites">Prerequisites</h2>

<ul>
  <li>docker (You can use <a href="https://get.docker.com/">this convinient script</a>)</li>
  <li>rust/cargo (You can install Rust by <code class="language-plaintext highlighter-rouge">rustup</code>)</li>
</ul>

<h2 id="getting-cita-optional-skip-if-you-are-using-an-existing-chain-such-as-test-chain">Getting CITA (OPTIONAL, skip if you are using an existing chain such as test chain)</h2>

<p>So, first we need to download cita. There are two options:</p>

<ol>
  <li>download the latest source file and compile</li>
</ol>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone https://github.com/cryptape/cita.git &amp;&amp; \
cd cita &amp;&amp; \
git submodule init &amp;&amp; \
git submodule update &amp;&amp; \
./env.sh make release 
</code></pre></div></div>

<p>or you can compile the <code class="language-plaintext highlighter-rouge">debug</code> version by <code class="language-plaintext highlighter-rouge">./env.sh make debug</code></p>

<ol>
  <li>download the release version</li>
</ol>

<p>You can find release version <a href="https://github.com/cryptape/cita/releases">there</a></p>

<h2 id="createrun-nodes--setup-an-app-chain-optional-skip-if-you-are-using-an-existing-chain-such-as-test-chain">Create/Run nodes &amp;&amp; Setup an app chain (OPTIONAL, skip if you are using an existing chain such as test chain)</h2>

<p>Take a look at <a href="https://github.com/cryptape/cita/tree/develop/tests/integrate_test"><code class="language-plaintext highlighter-rouge">tests/integration_test/</code></a>, it contains the all the methods that cita provides. We can use <code class="language-plaintext highlighter-rouge">cita_start.sh</code> to generate and run 4 local nodes. You will find the generated files at <code class="language-plaintext highlighter-rouge">target/install/test-chain</code>.</p>

<p>For multiple chains, you can run</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./env.sh ./scripts/create_cita_config.py create --chain_name test2-chain --jsonrpc_port 2337 --ws_port 5337 --grpc_port 6000 --nodes "127.0.0.1:8000,127.0.0.1:8001,127.0.0.1:8002,127.0.0.1:8003"

</code></pre></div></div>
<p>to generate a test2-chain but notice multiple chains are runned in one docker container because of the limitation of <code class="language-plaintext highlighter-rouge">rabbitmq</code>.</p>

<h3 id="create-side-chain-with-a-main-chain">Create side chain with a main chain</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./env.sh ./scripts/create_cita_config.py create \
--chain_name test2-chain \
--jsonrpc_port 2337 \
--ws_port 5337 \
--grpc_port 6000 \
--nodes "127.0.0.1:8000,127.0.0.1:8001,127.0.0.1:8002,127.0.0.1:8003" \
--contract_arguments "SysConfig.chainId=3" "SysConfig.economicalModel=1" \
"ChainManager.parentChainId=2" "ChainManager.parentChainAuthorities=0x17f3487df9f9331969602bf203165abf886a0ed1"
</code></pre></div></div>

<p>You can check the <code class="language-plaintext highlighter-rouge">parentChainAuthorities</code> at <code class="language-plaintext highlighter-rouge">test-chain/template/authorities.list</code>. The example only uses the auth of node0 created by default.</p>

<h2 id="check-the-status-optional-skip-if-you-are-using-an-existing-chain-such-as-test-chain">Check the status (OPTIONAL, skip if you are using an existing chain such as test chain)</h2>

<p>An elegant way to do this is install <a href="https://github.com/cryptape/cita-cli">cita-cli</a></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone https://github.com/cryptape/cita-cli.git
cd cita-cli/cita-cli
cargo install
</code></pre></div></div>

<p>Then you can run it (interactive mode) at <code class="language-plaintext highlighter-rouge">~/.cargo/bin/cita-cli</code> or <code class="language-plaintext highlighter-rouge">cargo run</code> under <code class="language-plaintext highlighter-rouge">cita-cli/cita-cli</code></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># default is http://127.0.0.1:1337
# the following is a test chain provided by nervos
cita&gt; switch --host http://121.196.200.225:1337
cita&gt; rpc blockNumber
...
cita&gt; exit
</code></pre></div></div>

<p>The full list of <code class="language-plaintext highlighter-rouge">json-rpc</code> list is <a href="https://docs.nervos.org/cita/#/rpc_guide/rpc">here</a></p>

<h2 id="deploy-contracts">Deploy contracts</h2>

<h4 id="the-default-config-super_admin-account-for-local-test-chain">The default config super_admin account for local test-chain</h4>

<ul>
  <li>private key: <code class="language-plaintext highlighter-rouge">5f0258a4778057a8a7d97809bd209055b2fbafa654ce7d31ec7191066b9225e6</code></li>
  <li>address: <code class="language-plaintext highlighter-rouge">0x4b5ae4567ad5d9fb92bc9afd6a657e6fa13a2523</code></li>
</ul>

<h4 id="the-test-chain-provided-by-nervos">The test chain provided by nervos</h4>

<ul>
  <li><code class="language-plaintext highlighter-rouge">http://121.196.200.225:1337</code></li>
  <li>private key: <code class="language-plaintext highlighter-rouge">0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee</code></li>
</ul>

<p>The deployment of this tutorial follows the example of <a href="https://github.com/cryptape/dapp-demos">dapp-demos/first-forever</a>.</p>

<ol>
  <li>Compile the contract by <code class="language-plaintext highlighter-rouge">solc</code></li>
</ol>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>solcjs --optimized --abi --bin &lt;your contract&gt;
</code></pre></div></div>

<ol>
  <li>Copy the abi &amp; bin to <code class="language-plaintext highlighter-rouge">compiled.js</code> (actually we can automate this process with node, take a look at <a href="https://gist.github.com/tomconte/4edb83cf505f1e7faf172b9252fff9bf#file-web3-solc-contract-compile-deploy-js-L9-L12">this example</a>, but I was not able to do this due to some unknow reason)</li>
  <li><code class="language-plaintext highlighter-rouge">npm run deploy</code> and you will get a contract address (You probably want to note it down for later use)</li>
</ol>

<h2 id="integrate-contracts-to-dapp">Integrate contracts to dapp</h2>

<p>You can use the <a href="https://github.com/cryptape/nervos.js/tree/develop/packages/nervos-chain"><code class="language-plaintext highlighter-rouge">nervos.js</code></a> which is a sdk for cita.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>const abi = JSON.parse(
  '[{"constant":false,"inputs":[{"name":"_value","type":"uint256"}],"name":"set","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}]',
)
const contract = new nervos.appchain.Contract(abi, contractAddress)

// call method
// get method is specified by contract through abi
// contract.methods.myMethod(paramters).call(transaction)
contract.methods.get().call()

// send method
// set method is specified by contract through abi
// contract.methods.myMethod(parameters).send(transaction)
contract.methods.set(5).send(transaction)
</code></pre></div></div>

<h2 id="final-thoughts">Final thoughts</h2>

<p>I just learned about CITA (nervos) two days ago at a hacakthon. It really is a developer-friendly blockchain system. I got prompt response from the maintainers on Wechat group and on site at hackathon. There are some improvements needed, but overall it is a pleasure experience.</p>

<p>For the hackathon, our team did an decentralized random number generator - Thanos based on Nervos (<a href="https://github.com/PRIEWIENV/NurTib">link</a>). Although we did not win a prize, we think this could be an important infrastructure for Nervos system because random number generator could power up many applications in games, cryptographs, …</p>]]></content><author><name></name></author><category term="tutorial" /><category term="blockchain" /><category term="tutorial" /><summary type="html"><![CDATA[Although the documentation of cita is already in details, there are some tricks that I would like to share.]]></summary></entry></feed>