<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>笔记 &#8211; 寂静花园</title>
	<atom:link href="https://www.addesp.com/archives/category/%E7%AC%94%E8%AE%B0/feed" rel="self" type="application/rss+xml" />
	<link>https://www.addesp.com</link>
	<description>寂静中交织着热情</description>
	<lastBuildDate>Sat, 09 Nov 2024 13:30:51 +0000</lastBuildDate>
	<language>zh-Hans</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1.2</generator>
	<item>
		<title>LLVM IR 学习笔记（一）</title>
		<link>https://www.addesp.com/archives/5650</link>
					<comments>https://www.addesp.com/archives/5650#comments</comments>
		
		<dc:creator><![CDATA[ADD-SP]]></dc:creator>
		<pubDate>Fri, 01 Sep 2023 15:58:24 +0000</pubDate>
				<category><![CDATA[LLVM]]></category>
		<category><![CDATA[笔记]]></category>
		<guid isPermaLink="false">https://www.addesp.com/?p=5650</guid>

					<description><![CDATA[学了也不知道有没有用。]]></description>
										<content:encoded><![CDATA[
<h1 class="wp-block-heading">什么是 LLVM IR</h1>



<p class="wp-block-paragraph">IR（Intermediate Representation）即中间表示，是程序编译器处理源代码时生成的中间的代码，它具有下列特点。</p>



<ul class="wp-block-list">
<li>等效于源代码</li>



<li>可读性低于源代码</li>



<li>相比于源代码更容易转化为机器码</li>



<li>相比于源代码更容易执行代码优化</li>
</ul>



<p class="wp-block-paragraph">程序编译器通过将源代码转化为 IR 来使得一些编译工作更简单，不过如果是一些比较简单的编程语言有时也会直接将源码翻译为机器码。IR 是业内常用的一种处理方式，并非强制性的技术。</p>



<p class="wp-block-paragraph">LLVM 是模块化的，包含一系列可重用的编译器工具链的集合，所以 LLVM IR 就是 LLVM 使用的 IR。</p>



<h1 class="wp-block-heading">LLVM IR 的三种形式</h1>



<p class="wp-block-paragraph">LLVM IR 有三种等价的表现形式。</p>



<ol class="wp-block-list">
<li>内存的编译器 IR。</li>



<li>存储在硬盘上的二进制 IR：可以快速地加载，比较适合 JIT 编译器。</li>



<li>人类可读的汇编 IR：一种汇编语法，可以被人类简单地理解。</li>
</ol>



<p class="wp-block-paragraph">本文也让将使用第三种形式的 IR 来介绍（不会有人愿意看二进制吧）。</p>



<h2 class="wp-block-heading">标识符（Identifier）</h2>



<p class="wp-block-paragraph">LLVM IR 的标识符分为两种类型。</p>



<ul class="wp-block-list">
<li>全局标识符：以 <code>@</code> 开头的符合标准的字符串。</li>



<li>局部标识符：以 <code>%</code> 开头的符合标准的字符串。</li>
</ul>



<p class="wp-block-paragraph">所谓符合标准的字符串，其实使用的这个正则 <code>[-a-zA-Z$._][-a-zA-Z$._0-9]*</code>，所以下列标识符都是合法的。</p>



<ul class="wp-block-list">
<li><code>%foo</code></li>



<li><code>@DivisionByZero</code></li>



<li><code>%a.really.long.identifier</code></li>
</ul>



<p class="wp-block-paragraph">比较特殊的是，如果前缀后的字符串可被理解为一个非负整数，比如 <code>@12</code>，这种标识符被叫做【未命名标识符】，它便编译器快速生成临时变量，不必将注意力分散到避免字符串重复的工作。</p>



<p class="wp-block-paragraph">使用前缀来定义标识符的好处是不会和保留字冲突，比如保留字 <code>mul</code> 表示乘法，我们就不用担心有一个同名的标识符。</p>



<p class="wp-block-paragraph">举个例子来看看 IR 看起来长什么样。</p>



<pre class="wp-block-code language-plain"><code>%result = mul i32 %x, 8</code></pre>



<p class="wp-block-paragraph">这段 IR 的作用是将变量 <code>%x</code> 乘以 8 并赋值给变量 <code>%result</code>，此外还有几种等价的形式。</p>



<pre class="wp-block-code language-plain"><code>%result = shl i32 %x, 3
; ---------
%0 = add i32 %X, %X           ; yields i32:%0
%1 = add i32 %0, %0           ; yields i32:%1
%result = add i32 %1, %1</code></pre>



<h1 class="wp-block-heading">模块（Module）</h1>



<p class="wp-block-paragraph">LLVM 程序由若干个模块组成，每个模块都是一个最小的翻译单元，且每个模块都由函数，全局变量和符号表组成。</p>



<p class="wp-block-paragraph">多个模块可以被 LLVM linker 组合在一起，这样可以合并符号表，也可以处理前置声明。</p>



<p class="wp-block-paragraph">下面是一个简单的 LLVM 模块。</p>



<pre class="wp-block-code language-plain"><code>; Declare the string constant as a global constant.
@.str = private unnamed_addr constant &#91;13 x i8] c"hello world\0A\00"

; External declaration of the puts function
declare i32 @puts(ptr nocapture) nounwind

; Definition of main function
define i32 @main() {
  ; Call puts function to write out the string to stdout.
  call i32 @puts(ptr @.str)
  ret i32 0
}

; Named metadata
!0 = !{i32 42, null, !"string"}
!foo = !{!0}</code></pre>



<p class="wp-block-paragraph">这个模块作用就是输出字符串 <code>hello world!</code>，最后几行是一些元数据，我们暂时不用关心它们。</p>



<h1 class="wp-block-heading">链接类型（Linkage Types）</h1>



<p class="wp-block-paragraph">每个函数和全局变量都有一个链接类型，下面将简单介绍几种类型。</p>



<ul class="wp-block-list">
<li>external：默认链接类型，它是全局外部可见的，这种类型参与链接并可以被作为外部符号被解析。</li>



<li>private：仅在当前的 LLVM 模块中可见。</li>



<li>internal：与 private 相同，只是在 ELF 文件中的符号格式不太一样。</li>
</ul>



<p class="wp-block-paragraph">其余的类型请参考<a href="https://llvm.org/docs/LangRef.html#linkage-types" target="_blank" rel="noopener" title="官方文档">官方文档</a>。</p>



<h1 class="wp-block-heading">调用约定（Calling Conventions）</h1>



<p class="wp-block-paragraph">LLVM 的一些 IR 可以用来调用某个函数，比如 <code>call</code> 和 <code>invoke</code>，有时我们需要显示地写明调用约定。</p>



<p class="wp-block-paragraph">简单介绍几种调用约定：</p>



<ul class="wp-block-list">
<li>ccc：标准 C 语言调用约定。</li>



<li>fastcc：一种尽可能快的调用约定，它并非指代某个特定的调用约定，而是将选择权交给编译器。这就要求函数声明和调用时都必须显式地声明此调用约定。</li>



<li>coldcc：一种对调用方性能友好的调用约定，它假设这个函数很少被调用，基于此假设可以做一些代码优化。它同样不指代某个特定的调用约定，而是将选择权交给编译器。</li>
</ul>



<p class="wp-block-paragraph">其余的调用约定请参考<a href="https://llvm.org/docs/LangRef.html#calling-conventions" target="_blank" rel="noopener" title="官方文档">官方文档</a>。</p>



<h1 class="wp-block-heading">可见性（Visibility Styles）</h1>



<p class="wp-block-paragraph">每个函数和全局变量有可见性约束。</p>



<ul class="wp-block-list">
<li>default：
<ul class="wp-block-list">
<li>EFL Object file：对于其它模块是可见的。</li>



<li>ELF Shared Libraries：这个符号是可以被覆盖的。</li>



<li>&#8230;</li>
</ul>
</li>



<li>hidden：一般是外部不可见的。</li>



<li>protected：对于 ELF 文件，这表示该符号是外部可见的，但是不允许外部修改。</li>
</ul>



<p class="wp-block-paragraph">如果你的符号的链接类型是 <code>private</code> 或者 <code>internal</code>，那么它的可见性必须是 <code>default</code>。</p>



<h1 class="wp-block-heading">结构类型（Structure Type）</h1>



<p class="wp-block-paragraph">结构类型是若干个类型的集合，类似于 C 语言中的结构体。</p>



<pre class="wp-block-code language-plain"><code>%T1 = type { &lt;type list&gt; }     ; Identified normal struct type
%T2 = type &lt;{ &lt;type list&gt; }&gt;   ; Identified packed struct type</code></pre>



<p class="wp-block-paragraph">使用 <code>{}</code> 定义的结构类型会有 padding 来优化访问性能，而使用 <code>&lt;{}&gt;</code> 定时的结构类型则是不会有任何 padding，十分紧凑。</p>



<h2 class="wp-block-heading">不透明结构类型（Opaque Structure Types）</h2>



<p class="wp-block-paragraph">类似于结构类型，只是我们不知道它的结构是什么，有点类似于前置声明的类型。</p>



<pre class="wp-block-code language-plain"><code>%X = type opaque
%52 = type opaque</code></pre>



<p class="wp-block-paragraph"></p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.addesp.com/archives/5650/feed</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Windows10 Cryptographic Services 占用过高</title>
		<link>https://www.addesp.com/archives/5599</link>
					<comments>https://www.addesp.com/archives/5599#comments</comments>
		
		<dc:creator><![CDATA[ADD-SP]]></dc:creator>
		<pubDate>Thu, 27 Jan 2022 14:12:10 +0000</pubDate>
				<category><![CDATA[笔记]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[故障排除]]></category>
		<guid isPermaLink="false">https://www.addesp.com/?p=5599</guid>

					<description><![CDATA[经常会在任务管理器内看到  Cryptographic Services 的 CPU 占用和硬盘占用很高，以至于降低系统变卡。手动关闭它过段时间还会自己出现，禁用这个服务过段时间后也会自动出现。]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading" id="症状">症状</h2>



<p class="wp-block-paragraph">经常会在任务管理器内看到  Cryptographic Services 的 CPU 占用和硬盘占用很高，以至于降低系统变卡。手动关闭它过段时间还会自己出现，禁用这个服务过段时间后也会自动出现。</p>



<h2 class="wp-block-heading" id="解决">解决</h2>



<ol class="wp-block-list"><li>右键 “此电脑”。</li><li>点击 “管理“。</li><li>双击 ”服务和应用程序“。</li><li>双击 ”服务“。</li><li>找到  “Cryptographic Services”，右键单击，点击 ”停止“。</li><li>找到文件夹 ”C:\Windows\System32\catroot2”，将其重命名为 “catroot2.old”。</li><li>重启服务 “Cryptographic Services”。</li></ol>



<p class="wp-block-paragraph">重启后这个文件夹应该会被自动创建，然后资源占用过高的问题应该就解决了（至少在博主的电脑上是解决了）。</p>



<h2 class="wp-block-heading" id="原理">原理</h2>



<p class="wp-block-paragraph">什么？Windows10 的东西你还想搞明白原理（玄学）？你太猖狂了，地球人！</p>



<h2 class="wp-block-heading" id="参考资料">参考资料</h2>



<p class="wp-block-paragraph"><a href="https://www.tecklyfe.com/fix-cryptographic-service-100-and-high-disk-usage/" target="_blank" rel="noreferrer noopener">Fix Cryptographic Services 100% and High Disk Usage &#8211; TeckLyfe</a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.addesp.com/archives/5599/feed</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>BitTorrent 扩展协议（Extension Protocol）详解</title>
		<link>https://www.addesp.com/archives/5563</link>
					<comments>https://www.addesp.com/archives/5563#respond</comments>
		
		<dc:creator><![CDATA[ADD-SP]]></dc:creator>
		<pubDate>Tue, 18 Jan 2022 09:43:19 +0000</pubDate>
				<category><![CDATA[BitTorrent 协议簇详解]]></category>
		<category><![CDATA[通信协议]]></category>
		<guid isPermaLink="false">https://www.addesp.com/?p=5563</guid>

					<description><![CDATA[BitTorrent 扩展协议（Extension Protocol）为  BitTorrent  协议簇提供了一个简单而轻量的扩展方式，可以在保证兼容性的同时为协议加入新的功能。比如基于 DHT 协议的磁力链接就是使用扩展协议加入的新功能。
本文将介绍 BT 扩展协议的报文格式和时序。]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">概述</h2>



<p class="wp-block-paragraph">一个协议从制定之初就不可能是完美的，总是要根据具体情况扩展功能，但扩展功能的同时要考虑与旧版本协议的兼容性，否则当一个用户升级了新的协议后，可能之前的许多功能就不能用，因为可能大多数人还没有升级。</p>



<p class="wp-block-paragraph">BitTorrent 扩展协议（Extension Protocol）为  BitTorrent  协议簇提供了一个简单而轻量的扩展方式，可以在保证兼容性的同时为协议加入新的功能。比如基于 <a href="/archives/5428" target="_blank" rel="noreferrer noopener" title="DHT 协议">DHT 协议</a> 的磁力链接就是使用扩展协议加入的新功能。</p>



<p class="wp-block-paragraph">本文将介绍 BT 扩展协议的报文格式和时序。</p>



<h2 class="wp-block-heading">回顾“<a href="/archives/5271" target="_blank" rel="noreferrer noopener" title="伙伴（Peer）协议">伙伴（Peer）协议</a>”</h2>



<p class="wp-block-paragraph">伙伴（Peer）协议在完成握手后，为每一种消息都指定了一个唯一的 ID，并且每次发送消息时，都要在消息的最开始处填充本次消息的字节长度，这个长度也包含“长度”这项数据本身占据的空间。</p>



<p class="wp-block-paragraph">伙伴（Peer）协议中并没有可以用于扩展功能的消息类型，所以扩展协议为此新增了一个消息类型，并指定了唯一的 ID，即 <code>extended</code> 消息，ID 是 <code>20</code>。</p>



<h2 class="wp-block-heading">“扩展”消息</h2>



<p class="wp-block-paragraph">扩展消息的用处是完成扩展协议本身的握手流程，并且协助其它协议的工作，比如协助  <a href="/archives/5428" target="_blank" rel="noreferrer noopener" title="DHT 协议">DHT 协议</a> 传输 DHT 节点的 UDP 端口。</p>



<p class="wp-block-paragraph">扩展消息的 ID 为 20，当程序发送这一消息时，其布局是下面这样的，</p>



<figure class="wp-block-table is-style-stripes"><table class="has-fixed-layout"><tbody><tr><td class="has-text-align-center" data-align="center">第 0 ~ 3 个字节</td><td class="has-text-align-center" data-align="center">第 4 个字节</td><td class="has-text-align-center" data-align="center">第 5 个字节</td><td class="has-text-align-center" data-align="center">若干个字节</td></tr><tr><td class="has-text-align-center" data-align="center">整个消息的长度</td><td class="has-text-align-center" data-align="center">消息的 ID（20）</td><td class="has-text-align-center" data-align="center">扩展消息的 ID</td><td class="has-text-align-center" data-align="center">载荷（payload）</td></tr></tbody></table><figcaption>extended 消息布局</figcaption></figure>



<p class="wp-block-paragraph">报文中的第五个字节如果为 0 则表示这是握手消息，如果大于 0 则表示为其它协议的消息。报文中的载荷当扩展消息 ID 为 0 是是握手消息的载荷，其它情况下则由具体的协议规定，比如  <a href="/archives/5428" target="_blank" rel="noreferrer noopener" title="DHT 协议">DHT 协议</a> 就规定了载荷的格式。</p>



<h2 class="wp-block-heading">握手消息</h2>



<p class="wp-block-paragraph">握手消息应当在 <a href="/archives/5271" target="_blank" rel="noreferrer noopener" title="伙伴（Peer）协议">伙伴（Peer）协议</a> 完成握手后立即发送给对方，一方收到握手消息后，也可以向对方发送握手消息用来禁用某个协议，或者协商某个协议。</p>



<p class="wp-block-paragraph">握手消息的载荷是一个经过了 B 编码的字典。下面列出的所有的字典中的项目都是可选的，任何一个未知的项目都应该被忽略，字典的全部内容都是区分大小写的。</p>



<p class="wp-block-paragraph">这个字典中包含下列内容。</p>



<ul class="wp-block-list"><li>m：一个字典，从一个字符串映射到一个非负整数。字符串是受支持的协议的名称，非负整数是这个协议的 ID。协议的名称必须是唯一的，ID 则只需要保证对于通信双方唯一即可。如果 ID 为 0 则表示发送方希望禁用这个 ID 对应的协议。</li><li>p：一个整数，表示发送握手消息的一方监听的 TCP 端口号。对于握手消息的接收方，通常不需要发送这一项，因为对方只有在知道了监听端口后才能够向其发送握手消息。</li><li>v：客户端的名称（UTF-8），比如 “uTorrent 1.2”。</li><li>yourip：一个字符串，表示握手消息发送方看到的接收方的 IP 地址的紧凑表。消息的接收方可能持有多个 IP 地址，这一项可以告诉接收方它的哪个 IP 被发送方所使用。</li><li>ipv6：如果发送方持有一个或多个 IPV6 地址，则应该将这些 IPV6 地址以紧凑表的形式写入到这一项中。</li><li>ipv4：基本同 <code>ipv6</code>。</li><li>reqq：一个整数，表示发送方允许多少个消息排队处理，超出此数量后消息可能会被丢弃。</li></ul>



<p class="wp-block-paragraph">这种握手消息比较奇怪，通常来说，一方发送握手消息后，另一方会以另外的格式来回复握手消息，比如 TLS 的 ClientHello 消息和 ServerHello 消息。但这里握手消息无论是发送还是回复都遵循同一种格式，不过也意料之中，毕竟 P2P 网络中不区分客户端和服务端。</p>



<p class="wp-block-paragraph">举个例子来直观地了解一下握手消息。</p>



<p class="wp-block-paragraph"><code>d1:md11:LT_metadatai1e6:ut_pexi2ee1:pi6881e1:v12:uTorrent 1.2e</code></p>



<p class="wp-block-paragraph">上面就是一个合法的握手消息的载荷，如果将其转化为 JSON 格式则是下面这样。</p>



<pre class="wp-block-code language-json"><code>{
    "m": {
        "LT_metadata": 1,
        "ut_pex": 2
    },
    "p": 6881,
    "v": "uTorrent 1.2"
}</code></pre>



<h2 class="wp-block-heading">参考资料</h2>



<p class="wp-block-paragraph"><a href="https://www.bittorrent.org/beps/bep_0010.html" target="_blank" rel="noreferrer noopener" title="Extension Protocol">Extension Protocol</a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.addesp.com/archives/5563/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>BitTorrent 分布式散列表（DHT）协议详解</title>
		<link>https://www.addesp.com/archives/5428</link>
					<comments>https://www.addesp.com/archives/5428#respond</comments>
		
		<dc:creator><![CDATA[ADD-SP]]></dc:creator>
		<pubDate>Sat, 16 Oct 2021 15:26:02 +0000</pubDate>
				<category><![CDATA[BitTorrent 协议簇详解]]></category>
		<category><![CDATA[分布式散列表]]></category>
		<category><![CDATA[通信协议]]></category>
		<guid isPermaLink="false">https://www.addesp.com/?p=5428</guid>

					<description><![CDATA[DHT 协议大幅度提高了 BitTorrent 网络的容错性，使整个网络难以因 Tracker 服务器的下线而崩溃，而这一切的外在表现就是“磁力链接”。]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">概述</h2>



<p class="wp-block-paragraph">DHT 协议大幅度提高了 BitTorrent 网络的容错性，使整个网络难以因 Tracker 服务器的下线而崩溃，而这一切的外在表现就是“磁力链接”。</p>



<p class="wp-block-paragraph">DHT 协议涉及到分布式散列表的相关知识，请参考<a href="/archives/5338" target="_blank" rel="noreferrer noopener" title="/archives/5338">分布式散列表协议 —— Kademlia 详解</a>。</p>



<p class="wp-block-paragraph">下文将 ”BitTorrent 软件“ 简称为 &#8220;BT 软件“。</p>



<h2 class="wp-block-heading">为什么需要 DHT 协议？</h2>



<p class="wp-block-paragraph"><a href="/archives/5271" target="_blank" rel="noreferrer noopener" title="/archives/5271">伙伴（Peer）协议</a>可以让 BitTorrent 节点之间互相交换已知信息来完整下载和上传。当然这一切的前提是我们能够知道其它节点的联系方式，没有了这个前提，我们只能以 0KB/s 的速度等待了。<a href="/archives/5271" target="_blank" rel="noreferrer noopener" title="/archives/5271">Tracker 协议</a>用于查询可以帮助下载和上传的节点，有了节点的联系方式我们才能通过伙伴（Peer）协议来交换数据完成下载和上传。但是 Tracker 是一个中心的服务器，一旦 Tracker 服务器下线，那么网络中的所有节点就只能抓瞎了。</p>



<p class="wp-block-paragraph">可见 Tracker 服务器是整个 BitTorrent 网络的弱点，究其本质其实是因为节点的联系方式都存储在一个固定的位置，容错性很差。如果将这些信息存储在整个 BitTorrent 网络中，那么少数节点的故障并不会导致联系方式的丢失，也就不会影响整个网络的运行。这就是 DHT 协议的任务 —— 将每个节点变成一个小型 Tracker。</p>



<h2 class="wp-block-heading">磁力链接</h2>



<p class="wp-block-paragraph">当你使用 BT 软件下载的时候你可能见过下面这样的地址。</p>



<pre class="wp-block-code language-URL"><code>magnet:?xt=urn:btih:c9e15763f722f23e98a29decdfae341b98d53056&amp;dn=Cosmos+Laundromat&amp;tr=udp%3A%2F%2Fexplodie.org%3A6969&amp;tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969&amp;tr=udp%3A%2F%2Ftracker.empire-js.us%3A1337&amp;tr=udp%3A%2F%Ftracker.leechers-paradise.org%3A6969&amp;tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337&amp;tr=wss%3A%2F%2Ftracker.btorrent.xyz&amp;tr=wss%3A%2F%2Ftracker.fastcast.nz&amp;tr=wss%3A%2F%2Ftracker.openwebtorrent.com&amp;ws=https%3A%2F%2Fwebtorrent.io%2Ftorrents%2F&amp;xs=https%3A%2F%2Fwebtorrent.io%2Ftorrents%2Fcosmos-laundromat.torrent</code></pre>



<p class="wp-block-paragraph">看起来很长，其实这个链接可以简化。</p>



<pre class="wp-block-code language-url"><code>magnet:?xt=urn:btih:c9e15763f722f23e98a29decdfae341b98d53056</code></pre>



<p class="wp-block-paragraph">严格来说上面的已经是最简版本了，但是对于现在的 BT 软件，如果你输入一个含有 40 个十六进制字符的字符串，它会自动帮你转为磁力链接。</p>



<pre class="wp-block-code language-url"><code>c9e15763f722f23e98a29decdfae341b98d53056</code></pre>



<p class="wp-block-paragraph">所以说最重要的就是这个十六进制字符串。你可以把用你的 BT 软件下载这段字符串，下载下来的视频还挺有趣的。</p>



<p class="wp-block-paragraph">上面这段十六进制字符串是通过哈希算法生成，每份资源丢到哈希算法里都会得到这样的一个字符串，并且仅属于这份资源（大概），也就是我们在之前的系列文章中提到的 <code>info_hash</code>。</p>



<h2 class="wp-block-heading">工作流程</h2>



<p class="wp-block-paragraph">当节点上线时它会随机生成一个节点 ID 并作为自己的 ID 使用。</p>



<p class="wp-block-paragraph">初始状态下我们并不知道其它节点的联系方式，此时需要通过其它方式或者，比如 Trakcer 服务器，或者 <code>.torrent</code> 文件中写明的联系方式。</p>



<p class="wp-block-paragraph">然后我们会与节点建立联系并构建路由表，并周期性地测试节点的联通状态，比如通信时延。通过此种方式我们获得了一个通信质量良好的节点列表。这时伙伴交换（Peer Exchange）协议也开始工作，我们会和越来越多的节点建立联系。</p>



<p class="wp-block-paragraph">当我们想下载一个文件时，我们计算文件 ID 和已知节点之间的距离，从自己的路由表中选择距离最近的若干个节点发起查询请求，接收到请求的节点如果正好可以提供这个文件则可以直接开始下载，反之它们会从自己的路由表中选择距离最近的若干个节点返回。</p>



<p class="wp-block-paragraph">当我们向上传某些文件时（做种），我们会计算文件的 ID，然后将自己的联系方式存储到距离文件 ID 最近的若干个节点中。</p>



<h3 class="wp-block-heading">人话</h3>



<p class="wp-block-paragraph">我们做种时总会把自己的联系方式存储到一个很小的范围内，此范围由文件的 ID 决定。当我们想下载时，会根据文件 ID 计算出这个范围，然后从里找做种的人就行了。</p>



<h2 class="wp-block-heading">协议概述</h2>



<p class="wp-block-paragraph">DHT 协议是一个基于 <a href="/archives/5338" target="_blank" rel="noreferrer noopener" title="/archives/5338">Kademila 协议</a>并使用 UDP 传输的应用协议，它可以将每个节点变成一个小型 Tracker，避免中心化的 Tracker 服务器所带来的风险。</p>



<p class="wp-block-paragraph">DHT 协议的基础部分和  <a href="/archives/5338" target="_blank" rel="noreferrer noopener" title="/archives/5338">Kademila 协议</a>一致，比如节点的 ID 都是 160 位的二进制数，使用异或算法计算距离和使用相同方法构建路由表。不同的地方在于 DHT 协议更改了部分 RPC 过程，并将这些 RPC 集合命名为 KRPC 协议。</p>



<h2 class="wp-block-heading">KRPC 协议</h2>



<p class="wp-block-paragraph">KRPC 协议是一个由 UDP 数据包和其包裹的经过 B 编码字典组成的简洁的 RPC 协议。每次查询只会有一次响应，没有重发/重试这种机制。</p>



<p class="wp-block-paragraph">下面是三种消息类型。</p>



<ul class="wp-block-list"><li>query（查询）</li><li>response（响应）</li><li>error（错误）</li></ul>



<p class="wp-block-paragraph">下面是四类查询。</p>



<ul class="wp-block-list"><li>ping</li><li>find_node</li><li>get_peer</li><li>announce_peer</li></ul>



<p class="wp-block-paragraph">KRPC 消息是一个经过 B 编码的字典（dictionary），每个消息至少有三个 key，分别表示”事务 ID“、”消息类型“ 和 ”客户端名称和版本“。根据消息的不同可以有更多的 key。</p>



<h3 class="wp-block-heading">ping</h3>



<p class="wp-block-paragraph">此消息用于检查目标节点的状态，发起者应发送自己的节点 ID，接收者应回复自己的节点 ID。</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"><p>喂？听到了么？</p><p>听到了。</p></blockquote>



<h3 class="wp-block-heading">find_node</h3>



<p class="wp-block-paragraph">此消息用于查询指定节点的联系方式（IP 地址和端口），发起者发送目标节点的 ID，接收者会从自己的路由表中选出距离目标节点最近的 k 个节点 ID 并返回。这是一个递归的过程，详情请阅读<a href="/archives/5338" target="_blank" rel="noreferrer noopener" title="/archives/5338">分布式散列表协议 —— Kademlia 详解</a>。</p>



<h3 class="wp-block-heading">get_peers</h3>



<p class="wp-block-paragraph">此消息用于查询于指定的 <code>info_hash</code> 相关的节点的联系方式，功能类似 Trakcer 服务器。发起者应发送 <code>info_hash</code>，接收者应返回与 <code>info_hash</code> 有关的节点列表，如果接收者不知道哪些节点于 <code>info_hash</code> 有关，则从自己的路由表中选出 k 个距离 <code>info_hash</code> 最近的节点并返回。</p>



<h3 class="wp-block-heading">announce_peer</h3>



<p class="wp-block-paragraph">当一个节点于某个 <code>info_hash</code> 有关时（下载或上传），它应当发起此消息。发起者应发送自己的节点 ID、<code>info_hash</code> 和监听端口。接收者应存储接收到的联系方式，将 <code>info_hash</code> 和联系方式关联起来，用于响应 get_peers 消息。</p>



<h2 class="wp-block-heading">参考资料</h2>



<p class="wp-block-paragraph"><a href="https://www.bittorrent.org/beps/bep_0005.html" target="_blank" rel="noreferrer noopener" title="https://www.bittorrent.org/beps/bep_0005.html">DHT Protocol</a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.addesp.com/archives/5428/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>分布式散列表协议 —— Kademlia 详解</title>
		<link>https://www.addesp.com/archives/5338</link>
					<comments>https://www.addesp.com/archives/5338#respond</comments>
		
		<dc:creator><![CDATA[ADD-SP]]></dc:creator>
		<pubDate>Wed, 06 Oct 2021 14:42:14 +0000</pubDate>
				<category><![CDATA[笔记]]></category>
		<category><![CDATA[BitTorrent]]></category>
		<guid isPermaLink="false">https://www.addesp.com/?p=5338</guid>

					<description><![CDATA[Kademlia 协议由 Petar Maymounkov 和 David Mazières 设计。它可以在容易出错的环境（比如节点会毫无征兆地下线）中建立一张分布式散列表。]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">概述</h2>



<p class="wp-block-paragraph">散列表是一种由键值对组成的列表。你可以把它看作一部字典，只需要经过少数的几步就能通过某种信息找到需要的信息，查询速度很快。</p>



<p class="wp-block-paragraph">分布式散列表就是由一个网络内所有的节点共同维护的一种散列表，这类散列表通常十分巨大，或者压根不可能由单个机器或者某机组机器维护。</p>



<p class="wp-block-paragraph">Kademlia 协议由 Petar Maymounkov 和 David Mazières&nbsp;设计。它可以在容易出错的环境（比如节点会毫无征兆地下线）中建立一张分布式散列表。</p>



<h2 class="wp-block-heading">为什么需要分布式散列表？</h2>



<p class="wp-block-paragraph">当散列表不是很大的时候只需要存储在一个机器上即可，但是有时哈希表会十分大，大到不是几组机器能够维护。亦或者有些哈希表是无法进行集中式存储的，比如 BitTorrent 的 DHT 网络。</p>



<p class="wp-block-paragraph">以 BitTorrent 网络为例，每个文件都有一个文件 ID，文件 ID 是一个长度为 160  的二进制数。每个 BitTorent 节点也会有一个相同长度的节点 ID。文件 ID 和 节点 ID 之间是“多对多”的关系，想使用散列表集中式地存储这些信息几乎是不可能的，原因如下。</p>



<ul class="wp-block-list"><li>文件 ID 的取值空间十分巨大，即 $2^{160} \approx&nbsp;1.46 \times 10^{48}$。这个值有多大？全球一共有 70 亿人，每个人每秒下载 1,000,000 个文件， 大概需要 $6 \times 10^{16}$ 亿年才能下载完。</li><li>节点 ID 的取值空间也和文件 ID 的取值空间同样巨大。</li><li>节点 ID 和文件 ID 之间是多对多的关系，我们假设每个节点指对应两个文件 ID，那么共需要存储 $2^{161}$ 个项目，这几乎不可能。</li></ul>



<p class="wp-block-paragraph">由此可见，仅从空间成本上来看就是不可能的事情，更不要说节点随时会下线和故障等头疼的问题。</p>



<p class="wp-block-paragraph">分布散列表是由所有节点共同维护一个散列表，每个节点维护一部分，这样网络内就形成了一张巨大的散列表。</p>



<h2 class="wp-block-heading">前置知识</h2>



<p class="wp-block-paragraph">Kademlia 协议要求每个节点都有一个长度为 160 bit 的节点 ID。</p>



<h3 class="wp-block-heading">”异或“距离</h3>



<p class="wp-block-paragraph">Kademlia 协议使用”异或“算法来计算两个 ID 之间的距离，下面是一个例子。</p>



<pre class="wp-block-code language-plaintext"><code>假设节点ID 是 4 位二进制。

A 的 ID 为 0101
B 的 ID 为 1111

那么两者的距离就是 1010，换算成十进制就是 9。

    0 1 0 1
⊕  1 1 1 1
--------------
    1 0 1 0</code></pre>



<h3 class="wp-block-heading">二进制二叉树</h3>



<p class="wp-block-paragraph">每个节点都有一个长度固定的节点 ID，那么就可以将系统中的全部节点放到一个二叉树内，下面是一个例子。</p>



<pre class="wp-block-code language-plaintext"><code>假设节点 ID 的长度为 3 位二进制，则二叉树的每个叶子节点都代表了一个节点。

             *
           /   \
          /     \
      0  /       \  1
        /         \
       /           \
      *              *
    /  \           /  \
 0 /    \ 1     0 /    \ 1
  /      \       /      \
 O        O      O       O</code></pre>



<h3 class="wp-block-heading">信息的存储和查询</h3>



<p class="wp-block-paragraph">在  Kademlia 协议中，每个信息都有一个唯一的 ID，而发布这个信息的节点会将这个信息存储到距离距离信息 ID 最近的若干的节点中。可以认为是用全部已知节点 ID 和信息 ID 进行异或运算得到已知节点和信息之间的逻辑距离，然后选出若干个最近的节点，将信息存储到这些节点上。</p>



<p class="wp-block-paragraph">查询某个信息时，过程可以认为是用全部的已知节点 ID 和被查询的信息 ID 进行异或运算得到已知节点和信息的逻辑距离，然后选择出若干的最近的节点，然后向这些节点发起信息查询请求。</p>



<h2 class="wp-block-heading">路由表</h2>



<p class="wp-block-paragraph">Kademlia 协议依靠路由表和其它节点通信，下面介绍一下路由表的建立过程。</p>



<p class="wp-block-paragraph">前面我们提到网络中的节点都可以放到同一个二进制二叉树中，而路由表的制作过程实际上就是二叉树的拆分过程。我们假设节点 ID 为 11（二进制），看看二叉树的拆分过程。</p>



<p class="wp-block-paragraph">首先将不包含自身的最大子树取出。</p>



<pre class="wp-block-code language-plaintext"><code>+--------------+
|             *|
|           /  | \
|          /   |  \
|      0  /    |   \  1
|        /     |    \
|       /      |     \
|      *       |       *
|    /  \      |     /  \
| 0 /    \ 1   |  0 /    \ 1
|  /      \    |   /      \
| O        O   |  O        O
+--------------+</code></pre>



<p class="wp-block-paragraph">重复上述步骤，直到剩余的部分只有自己这一个节点。本例中只需要拆分两次。</p>



<pre class="wp-block-code language-plaintext"><code>+--------------+
|            * |
|           /  | \
|          /   |  \
|      0  /    |   \  1
|        /     |    \
|       /      |     \
|              | +-------+
|      *       | |      *|
|    /  \      | |    /  |\
| 0 /    \ 1   | | 0 /   | \ 1
|  /      \    | |  /    |  \
| O        O   | | O     |  O
+--------------+ +-------+</code></pre>



<p class="wp-block-paragraph">此时我们从左到右一共有了三颗子树，其中第三颗子树只有我们自己。此时三颗子树就对应了三个 K-桶（K-Bucket）。</p>



<figure class="wp-block-table is-style-regular"><table class="has-fixed-layout"><thead><tr><th class="has-text-align-center" data-align="center">编号</th><th class="has-text-align-center" data-align="center">覆盖前缀</th><th class="has-text-align-center" data-align="center">距离区间</th><th class="has-text-align-center" data-align="center">已经联系上的节点</th></tr></thead><tbody><tr><td class="has-text-align-center" data-align="center">1</td><td class="has-text-align-center" data-align="center">11</td><td class="has-text-align-center" data-align="center">0</td><td class="has-text-align-center" data-align="center">&#8230;</td></tr><tr><td class="has-text-align-center" data-align="center">2</td><td class="has-text-align-center" data-align="center">10</td><td class="has-text-align-center" data-align="center">1</td><td class="has-text-align-center" data-align="center">&#8230;</td></tr><tr><td class="has-text-align-center" data-align="center">3</td><td class="has-text-align-center" data-align="center">0?</td><td class="has-text-align-center" data-align="center">[2, 3]</td><td class="has-text-align-center" data-align="center">&#8230;</td></tr></tbody></table><figcaption>K-桶列表</figcaption></figure>



<p class="wp-block-paragraph">这就是我们的路由表了，此时我们可以通过下列步骤查询距离某个 key 最近的节点，假设我们要寻找的 key 为 00（二进制），我们自己的节点 ID 为 11（二进制）。</p>



<ol class="wp-block-list"><li>使用异或运算计算出自身和目标 key 的距离，计算结果为 2（十进制）。</li><li>查询 K-桶的距离区间，发现 3 号 K-桶中的节点距离目标 key 最近。</li><li>按照某种策略（见后文）选出三号 K-桶中的某个节点（假设为节点 01），然后向其发送查询请求。</li><li>节点 01 也会使用相同的方式建立自己的路由表，收到查询请求后它会执行下列步骤。<ol><li>使用异或运算计算出自身和目标 key 的距离，显然距离为 1（十进制）。</li><li>查询自身的 K-桶的距离区间，发现某个 K-桶的距离区间恰好为 1，并且此 K-桶内只有一个节点。</li><li>节点 01 发现自己已经存储了节点 00 的联系信息，于是将联系信息返回给节点 11。</li></ol></li><li>节点 00 收到了节点 01 的响应，得知了节点 00 的联系信息，查询结束。</li></ol>



<p class="wp-block-paragraph">这只是一个简单的例子，有很多错误，还有很多情况没有考虑到，但已经能够体现查询原理了。</p>



<h2 class="wp-block-heading">三个协议过程</h2>



<p class="wp-block-paragraph">Kademlia 由三个 RPC 过程组成，分别为 PING、STORE、FIND_NODE 和 FIND_VALUE。</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"><p>RPC（Remote Procedure Call），即远程过程调用。可以简单地认为是一方向另一方发送某个指令，接收方执行对应的指令并将结果返回给发送方。</p></blockquote>



<h3 class="wp-block-heading">PING</h3>



<p class="wp-block-paragraph">用来测试节点是否在线。</p>



<h3 class="wp-block-heading">STORE</h3>



<p class="wp-block-paragraph">请求目标节点存储一个键值对。</p>



<h3 class="wp-block-heading">FIND_NODE</h3>



<p class="wp-block-paragraph">查询距离目标键值最近的若干的节点。</p>



<p class="wp-block-paragraph">这个才是重点，本文将在这里详细描述查询过程。</p>



<p class="wp-block-paragraph">此过程的参数是一个 160 bit 的节点 ID，即要查询目标节点的 ID。此过程结束后返回若干个三元组 <code>&lt; IP, UDP port, Node ID &gt;</code>，每个三元组都是某个节点的联系信息。</p>



<p class="wp-block-paragraph">当一个节点收到 FIND_NODE 请求时，它会查询自己的 K-桶列表并返回 k 个距离目标节点最近的节点，如果此节点已知的节点不足 k 个则返回全部已知的节点。</p>



<p class="wp-block-paragraph">当一个节点想要查询某个节点时，它会执行下列步骤。</p>



<ol class="wp-block-list"><li>查询自己的 k 桶列表并选出若干个距离目标最近的节点，然后向这些节点发送 FIND_NODE 请求。</li><li>当这些 FIND_NODE 请求返回时我们会得到许多个节点，记录这些新的节点。</li><li>从新得到的节点中挑选出若干个距离目标节点更近（相对于所有旧的节点）的节点，向这些节点发送 FIND_NODE 请求。如果没有找到距离更近的节点则查询结束</li><li>重复步骤 2，3，直到查询结束。</li></ol>



<p class="wp-block-paragraph">上面的步骤依然是简化的版本，但步骤已经完整了，只是跳过了一些细节，比如每次选择多少个节点发送请求。</p>



<h3 class="wp-block-heading">FIND_VALUE</h3>



<p class="wp-block-paragraph">基本和 FIND_NODE 差不多，只不过如果收到请求的节点存有被请求的值得时候应该将其值返回。</p>



<h2 class="wp-block-heading">信息的再发布</h2>



<p class="wp-block-paragraph">有两种情况可能会导致已经存在的信息却不能被查询到。</p>



<ul class="wp-block-list"><li>存储了这些信息的全部节点下线。</li><li>有许多距离更近的节点加入到了网络中，查询信息时则会向这些节点发起请求，而不是距离更远的但是之前已经存储了信息的节点。</li></ul>



<p class="wp-block-paragraph">为了缓解这一情况，每个节点会以一小时为周期，每个周期内重新发布一次信息。但是这么做的代价很大。假设有 A 个节点存储了同一个信息，那么每个周期内，每个节点都会先发起一次 FIND_NODE 请求，然后每个节点都会向其它最多 A-1 个节点发起 STORE 请求。这样网络中就会充斥着大量的请求，并且很多是无用的请求。</p>



<p class="wp-block-paragraph">好在我们有优化方法，即当某个节点在本周期内收到 STORE 请求时，下一个周期内就不进行对该信息的再发布。</p>



<h2 class="wp-block-heading">典型应用</h2>



<p class="wp-block-paragraph">BitTorrent 协议簇中的分布式散列表（DHT）协议及由此构成的属于 BitTorrent 网络内部的 DHT 网络。正是有了 DHT 网络，我们才能够在没有 <code>.torrent</code> 文件的情况下凭借磁力链接下载文件。我们有时候在 BitTorrent 客户端中粘贴一段十六进制字符串就能进行下载也是 DHT 网络的功劳。</p>



<h2 class="wp-block-heading">参考资料</h2>



<p class="wp-block-paragraph">Kademlia: A Peer-to-peer information system based on the XOR Metric</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.addesp.com/archives/5338/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>BitTorrent Tracker 协议详解</title>
		<link>https://www.addesp.com/archives/5313</link>
					<comments>https://www.addesp.com/archives/5313#comments</comments>
		
		<dc:creator><![CDATA[ADD-SP]]></dc:creator>
		<pubDate>Sun, 03 Oct 2021 13:09:11 +0000</pubDate>
				<category><![CDATA[BitTorrent 协议簇详解]]></category>
		<category><![CDATA[通信协议]]></category>
		<guid isPermaLink="false">https://www.addesp.com/?p=5313</guid>

					<description><![CDATA[BitTorrent 网络内的主机依靠互相交换自身持有的资源来完成资源共享，而 Tracker 协议会告诉你哪些主机有你需要的资源。]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">概述</h2>



<p class="wp-block-paragraph">BitTorrent 网络内的主机依靠互相交换自身持有的资源来完成资源共享，而 Tracker 协议会告诉你哪些主机有你需要的资源。</p>



<p class="wp-block-paragraph">本文将详细介绍 Tracker 协议。</p>



<h2 class="wp-block-heading">基于 HTTP 的 Tracker 协议</h2>



<p class="wp-block-paragraph">Tracker 服务器可以使用 HTTP 协议对外提供服务，BitTorrent 客户端只需要向指定的 URL 发送 HTTP GET 请求即可。</p>



<p class="wp-block-paragraph">一个常见的 Tracker URL 可能是这样的：<code>http://tracker.example.org:80/announce</code>。发送 GET 请求时需要附带下列查询字符串。</p>



<ul class="wp-block-list"><li>info_hash：由 <code>.Torrent</code> 文件中的信息计算出的 SHA-1 摘要，并使用 B 编码进行编码。</li><li>peer_id：BitTorrent 客户端启动时随机生成的长度为 20 个字符串。</li><li>ip：可选参数，表示发起请求的 客户端的 IP。一般情况下会在 Tracker 和客户端在同一主机上时使用。</li><li>port：BitTorrent 客户端监听的端口号，此端口号将用于接收其它 BitTorrent 客户端的传入连接。</li><li>uploaded：已经上传的数据总量（字节）。</li><li>downloaded：已经下载的数据总量（字节）。</li><li>left：还需要下载的数据总量（字节）。此数值不能使用参数 <code>downloaded</code> 计算，因为 <code>downloaded</code> 参数包含了那些下载了但是没有通过校验的数据。</li><li>event：可选参数，可能取值为 <code>start</code>、<code>completed</code> 和 <code>stopped</code>。可以分别在下载开始、下载完成和停止下载时发送。</li></ul>



<hr class="wp-block-separator"/>



<p class="wp-block-paragraph">Tracker 服务器收到 GET 请求时应该响应一个使用 B 编码进行编码的字典，如果请求成功则字典中必须包含下列两个键。</p>



<ul class="wp-block-list"><li>interval：一个数字，表示应该等待多少秒后才能重新请求本 Trakcer。</li><li>peers：一个由字典组成的列表，每个列表包含了提供被请求资源的 Peer 的 ID，IP 和端口。</li></ul>



<p class="wp-block-paragraph">如果请求成功，则返回经过 B 编码编码后字典，如果将其使用 JSON 表示则是下面这样。</p>



<pre class="wp-block-code language-json"><code>{
    "interval": 120,
    "peers": &#91;
        { "peer id": "xxxxxxxxxxxxxxxxxxxx", "ip": "192.168.1.1", "port": 7000 },
        { "peer id": "yyyyyyyyyyyyyyyyyyyy", "ip": "192.168.1.2", "port": 7001 }
    ]
}</code></pre>



<p class="wp-block-paragraph">如果请求失败则同样需要返回一个经过 B 编码编码后的字典，且必须包含一个名为 <code>failure reason</code> 的键，其内容是人类可读的错误提示信息。</p>



<p class="wp-block-paragraph">由于 Tracker 的响应可能会很大，比如返回了大量的 Peer 信息，此时再使用 B 编码进行表示则会占用较大的空间。如果客户端不希望这种情况发生，可以在请求时添加一个名为 <code>compact</code> 的参数，并将其值设置为 <code>1</code>，表示建议 Tracker 返回经过紧凑的信息。如果其值为 <code>0</code>，则表示建议 Tracker 返回未经紧凑的信息。</p>



<p class="wp-block-paragraph">所谓紧凑的信息实际就是将键 <code>Peers</code> 的内容转化为二进制发送。下面是一个例子。</p>



<pre class="wp-block-code language-plaintext"><code>// 紧凑前
d8:intervali:120e5:peersl7:peer id20:xxxxxxxxxxxxxxxxxxxx11:192.168.1.1i:7000ee

// 紧凑后 Peer ID 被丢弃
d8:intervali:120e5:peers6:\x1\x1\xA8\xC0\x58\x1B</code></pre>



<h2 class="wp-block-heading">基于 UDP 的 Tracker 协议</h2>



<p class="wp-block-paragraph">基于 HTTP 的 Tracker 协议需要维护一个 TCP 连接，但是通常 Tracker 的请求和响应都不是很大，维护 TCP 连接的代价相比较有点大，于是便有了基于 UDP 的 Tracker 协议。</p>



<p class="wp-block-paragraph">下面是通信流程，IPV6 版本的类似。</p>



<pre class="wp-block-code language-plaintext"><code>Peer                                                        Tracker

Connect Request
+ 8 byte protocol_id
+ 4 byte action ( = 0 )
+ 4 byte transaction_id     -------------------------&gt;
                                                            Connect Response
                                                            + 4 byte action ( = 0 )
                                                            + 4 byte transaction_id
                                                            + 8 byte connection_id
                            &lt;-------------------------
IPV4 Announce Request
+ 8 byte connection_id
+ 4 byte action ( = 1 )
+ 4 byte transaction_id
+ 20 byte string info_hash
+ 20 byte string peer_id
+ 8 byte downloaded
+ 8 byte left
+ 8 byte uploaded
+ 4 byte event
+ 4 byte IP address
+ 4 byte key
+ 4 byte num_want
+ 2 byte port               -------------------------&gt;
                                                            IPV4 Announce Response
                                                            + 4 byte action ( = 1 )
                                                            + 4 byte transaction_id
                                                            + 4 byte interval
                                                            + 4 byte leechers
                                                            + 4 byte seeders
                                                            + 4 * n byte IP address
                                                            + 2 * n byte TCP port
                            &lt;-------------------------      + more</code></pre>



<p class="wp-block-paragraph">大多数参数都已经在 HTTP 版本中出现，此处只说明一些未出现的参数和有改动的参数。</p>



<ul class="wp-block-list"><li>transaction_id：由客户端随机生成的事务 ID，客户端使用此 ID 进行请求时，Tracker 服务器也必须使用同样的 ID 进行响应。</li><li>connection_id：由 Tracker 生成的连接 ID，客户端应该保存此 ID，并在后续请求中使用。Tracker 服务器也应该检查接收到的 ID 是否是预期的。</li><li>event：在 HTTP 版本的协议中此参数的取值为三个字符串或者空。在 UDP 版本中则不同。<ul><li>0 &#8211; none</li><li>1 &#8211; completed</li><li>2 &#8211; started</li><li>3 &#8211; stopped</li></ul></li><li>num_want：整数，表示希望获得的 peer 数量，默认为 <code>-1</code>。</li><li>leechers：整数，表示“吸血者”的数量。“吸血者”指下载多而上传少的用户。</li><li>seeders：整数，表示”做种者“的数量。</li></ul>



<h2 class="wp-block-heading">参考资料</h2>



<ul class="wp-block-list"><li><a href="https://www.bittorrent.org/beps/bep_0003.html" target="_blank" rel="noreferrer noopener">The BitTorrent Protocol Specification</a></li><li><a href="https://www.bittorrent.org/beps/bep_0015.html" target="_blank" rel="noreferrer noopener">UDP Tracker Protocol for BitTorrent</a></li><li><a href="https://www.bittorrent.org/beps/bep_0023.html">Tracker Returns Compact Peer Lists</a></li></ul>
]]></content:encoded>
					
					<wfw:commentRss>https://www.addesp.com/archives/5313/feed</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>BitTorrent 伙伴（Peer）协议详解</title>
		<link>https://www.addesp.com/archives/5271</link>
					<comments>https://www.addesp.com/archives/5271#comments</comments>
		
		<dc:creator><![CDATA[ADD-SP]]></dc:creator>
		<pubDate>Sat, 02 Oct 2021 15:10:12 +0000</pubDate>
				<category><![CDATA[BitTorrent 协议簇详解]]></category>
		<category><![CDATA[通信协议]]></category>
		<guid isPermaLink="false">https://www.addesp.com/?p=5271</guid>

					<description><![CDATA[本文将介绍 伙伴（Peer）协议的握手和通信流程。]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">概述</h2>



<p class="wp-block-paragraph">本文将介绍 伙伴（Peer）协议的握手和通信流程。</p>



<p class="wp-block-paragraph">伙伴（Peer）协议是一个运行在 TCP 或者 μTP 协议之上的应用层协议，被用于两个 BitTorrent 客户端之间的通信，如上传和下载。使用本协议通信时所有的字符串均使用 UTF-8 编码。</p>



<p class="wp-block-paragraph">协议可以分为握手流程和通信流程。</p>



<h2 class="wp-block-heading">握手流程</h2>



<p class="wp-block-paragraph">伙伴（Peer）协议由握手消息和无尽的数据流组成，也就是说协议没有规定断开连接前应该做什么，通信双方随时可以断开连接。</p>



<pre class="wp-block-code language-plaintext"><code>Peer A                                                         Peer B

Handshake
+ String "19:BitTorrAnt protocol"
+ Eight resvered bytes
+ 20 byte bencoded info_hash     
+ 20 byte peer id                  ----------------------&gt;
                                                               Handshake
                                                               + String "19:BitTorrAnt protocol"
                                                               + Eight resvered bytes
                                                               + Same 20 byte bencoded info_hash
                                                               + 20 byte peer id  

                                   &lt;----------------------


Application Data                   &lt;---------------------&gt;     Application Data</code></pre>



<p class="wp-block-paragraph">握手流程比较简单，A 首先依次发送下列信息。</p>



<ol class="wp-block-list">
<li>字符串 <code>19:BitTorrent protocol</code>。</li>



<li>八个保留的字节，目前的实现中全部为零。</li>



<li>一个使用 B 编码进行编码的 SHA-1 摘要，此摘要由 .torrent 文件中部分信息计算得出，在不考虑哈希碰撞的情况下每个摘要对应唯一份资源，也就是我们想要下载的资源。</li>



<li>客户端启动时随机生成的 ID。</li>
</ol>



<p class="wp-block-paragraph">B 收到握手消息后应当依次发送下列信息。</p>



<ol class="wp-block-list">
<li>字符串 <code>19:BitTorrent protocol</code>。</li>



<li>八个保留的字节，目前的实现中全部为零。</li>



<li>一个使用 B 编码进行编码的 SHA-1 摘要，要求于收到的摘要一致。</li>



<li>客户端启动时随机生成的 ID。</li>
</ol>



<p class="wp-block-paragraph">最后双方验证下列信息。</p>



<ol class="wp-block-list">
<li>发送的 SHA-1 摘要和接收到的 SHA-1 摘要是否相同。</li>



<li>对方的 ID 是否是自己所期望的。客户端一般都通过其它手段（如 Tracker 协议）提前获知了拥有某资源的 Peer ID 列表，如果对方的 ID 不在这份列表内则连接会被关闭。</li>
</ol>



<p class="wp-block-paragraph">如果上面两个条件同时满足则握手成功，反之则关闭连接。</p>



<h2 class="wp-block-heading">通信流程</h2>



<p class="wp-block-paragraph">握手完成后，接下来发送的任何数据都要遵循一个固定的格式，即”长度+具体数据“，也就是发送的数据中，开头是整个信息的字节大小，紧跟在后面的是”具体数据：，整个信息的字节大小不仅包含“具体数据”的大小，还包含开头的“长度”所占用的空间。</p>



<p class="wp-block-paragraph">通信流程中双方需要保持连接，因此一方可以发送一个长度为 0 的保活（keepalives）消息来确认连接的有效性，并且此消息应该被忽略。一般来说保活消息每两分钟发一次，如果对方有自己需要的数据，则周期可以更短。</p>



<p class="wp-block-paragraph">任何一个非保活消息都以一个单字节开始，不同的值对应着不同类型的消息，这个单字节被称为消息 ID。通信双方通过这些不同类型的消息来完成资源共享。</p>



<p class="wp-block-paragraph">消息 ID 有下列取值。</p>



<ul class="wp-block-list">
<li>0 &#8211; choke</li>



<li>1 &#8211; unchoke</li>



<li>2 &#8211; interested</li>



<li>3 &#8211; not interested</li>



<li>4 &#8211; have</li>



<li>5 &#8211; bitfield</li>



<li>6 &#8211; request</li>



<li>7 &#8211; piece</li>



<li>8 &#8211; cancel</li>
</ul>



<h3 class="wp-block-heading">Choke 消息</h3>



<p class="wp-block-paragraph">此消息不承载任何数据，也就是说发送方只需要发送一个消息 ID 即可。</p>



<p class="wp-block-paragraph">此消息表示在收到 “Unchoke 消息”之前，对方不会发送任何数据。</p>



<h3 class="wp-block-heading">Unchokie 消息</h3>



<p class="wp-block-paragraph">此消息不承载任何数据，也就是说发送方只需要发送一个消息 ID 即可。 </p>



<p class="wp-block-paragraph">如果收到了此消息，并且收到了“interested 消息”，则双方会开始数据传输。</p>



<h3 class="wp-block-heading">Interested 消息</h3>



<p class="wp-block-paragraph">此消息不承载任何数据，也就是说发送方只需要发送一个消息 ID 即可。</p>



<p class="wp-block-paragraph">此消息表示对方<strong>有兴趣</strong>进行数据传输。</p>



<h3 class="wp-block-heading">Not interested 消息</h3>



<p class="wp-block-paragraph">此消息不承载任何数据，也就是说发送方只需要发送一个消息 ID 即可。 </p>



<p class="wp-block-paragraph">此消息表示对方<strong>没有兴趣</strong>进行数据传输。</p>



<h3 class="wp-block-heading">Bitfield 消息</h3>



<p class="wp-block-paragraph">此消息承载了一个位图，表示消息的发送方已经下载了哪些数据块，因为数据是被分割为一个个的数据块的，所以可以根据位图的索引确定对方持有哪些数据块。</p>



<p class="wp-block-paragraph">此消息只能在握手完成后作为首个消息发送，如果没有持有任何数据块则可以不发送此消息。</p>



<h3 class="wp-block-heading">Have 消息</h3>



<p class="wp-block-paragraph">此消息承载一个数字，这个数字是某个数据块的索引，并且这个数据块是对方刚刚下载完毕并通过校验的数据块。</p>



<h3 class="wp-block-heading">Request 消息</h3>



<p class="wp-block-paragraph">此消息承载了数据块的索引，数据块内部的起始偏移（字节），和长度。这个长度通常是 2 的幂，除非它被文件末尾所截断。</p>



<h3 class="wp-block-heading">Cancel 消息</h3>



<p class="wp-block-paragraph">此消息承载了和”request 消息“相同的数据，用于取消之前请求的数据块。通常会在某个数据块即将或已经下载完成时发送，避免对方发送已经下载好的数据。</p>



<h3 class="wp-block-heading">Piece 消息</h3>



<p class="wp-block-paragraph">此消息承载了数据块索引，数据块内部偏移和具体的数据。</p>



<hr class="wp-block-separator has-css-opacity"/>



<p class="wp-block-paragraph">一般来说，客户端应该按照随机的顺序下载每个数据块，随机的顺序有利于让每个客户端持有不同的数据块，这就便于互相交换数据块，加速下载。</p>



<p class="wp-block-paragraph">当且仅当一方收到了”unchoke 消息“，另一方收到了”interested 消息“时，双方才会启动数据传输。</p>



<h2 class="wp-block-heading">参考资料</h2>



<p class="wp-block-paragraph"><a href="https://www.bittorrent.org/beps/bep_0003.html" target="_blank" rel="noreferrer noopener" title="https://www.bittorrent.org/beps/bep_0003.html">The BitTorrent Protocol Specification</a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.addesp.com/archives/5271/feed</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>BitTorrent 协议簇概述</title>
		<link>https://www.addesp.com/archives/5236</link>
					<comments>https://www.addesp.com/archives/5236#comments</comments>
		
		<dc:creator><![CDATA[ADD-SP]]></dc:creator>
		<pubDate>Fri, 01 Oct 2021 13:49:43 +0000</pubDate>
				<category><![CDATA[BitTorrent 协议簇详解]]></category>
		<category><![CDATA[通信协议]]></category>
		<guid isPermaLink="false">https://www.addesp.com/?p=5236</guid>

					<description><![CDATA[本文将简单介绍一些 BitTorrent 协议和一些术语，来让读者有一个整体的概念。]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">概述</h2>



<p class="wp-block-paragraph">本文将简单介绍一些 BitTorrent 协议和一些术语，来让读者有一个整体的概念。</p>



<h2 class="wp-block-heading">B 编码（bencoding）</h2>



<p class="wp-block-paragraph"> B 编码（bencoding）是用于编码协议消息的编码。Bittorrent 的许多协议中的消息都使用此种编码方式。此编码可以将字符串、整数、列表和字典编码成字符串。</p>



<p class="wp-block-paragraph">假设有一个字符串 <code>Hello World</code>，它会被编码为 <code>11:Hello World</code>。即 <code>长度:内容</code>。</p>



<p class="wp-block-paragraph">假设有一个整数 <code>256</code>，它会被编码为 <code>i:256e</code>。即开头是字母 <code>i</code>，后跟一个冒号 <code>:</code>，接着是十进制表示的数字，最后是一个字母 <code>e</code>。</p>



<p class="wp-block-paragraph">假设有一个列表 <code>[ 'spam', 'eggs' ]</code>，它会被编码成 <code>l4:spam4:eggse</code>。即以字母 <code>l</code> 开头，中间是列表的内容，并且内容也要使用对应的 B 编码进行编码，结尾一个字母 <code>e</code>。</p>



<p class="wp-block-paragraph">假设有一个字典 <code>{ 'cow': 'moo', 'spam': 'eggs'  }</code>，它会被编码成 <code>d3:cow3:moo4:spam4:eggse</code>。即以字母 <code>d</code> 开头，中间是内容，并且内容也要使用对应的 B 编码进行编码，结尾是一个字母 e。另一个例子是字典 <code>{ 'spam': [ 'a', 'b' ] }</code> 会被编码成 <code>d4:spaml1:a1:bee</code>。</p>



<h2 class="wp-block-heading">私有种子（Private Torrents）</h2>



<p class="wp-block-paragraph">一个种子（torrent）文件可以被标记为 ”私有的“，此时 BitTorrent 客户端应该禁用除了 Tracker 以外的所有 Peer 搜索方式，并且仅能使用私有的 Tracker。</p>



<h2 class="wp-block-heading">伙伴（Peer）协议</h2>



<p class="wp-block-paragraph">伙伴协议（Peer Protocol）也可以翻译为 “对等体协议” 或者 “同伴协议”，是用于与 Peer 通信以完成下载任务的协议。该协议底层可以使用 TCP 或者 μTP 传输。</p>



<h2 class="wp-block-heading">Tracker 协议</h2>



<p class="wp-block-paragraph"> Tracker 协议是用于发现伙伴（Peer）协议。</p>



<p class="wp-block-paragraph">有时我们并不知道哪些主机提供了我们需要的资源，这时你可以使用 Tracker 协议来获取这些信息，主机会使用 Tracker 协议来告知其它主机自己提供哪些资源，也可以使用 Tracker 协议得知某个资源被哪些主机提供。</p>



<h2 class="wp-block-heading">μTorrent 传输协议</h2>



<p class="wp-block-paragraph">BitTorrent 主机通常会开很多个连接来为许多个 Peer 服务。但是 TCP 会将带宽均匀地分配给每个连接，这就导致 BitTorrent 软件占用了大量的带宽，容易影响其它软件，特别是交互性强的软件如浏览器、游戏和聊天软件。</p>



<p class="wp-block-paragraph">μTorrent 传输协议是一个基于 UDP 的传输协议，它可以动态地调整带宽使用量，避免影响其它软件的网络通信。</p>



<h2 class="wp-block-heading">分布式散列表（Distributed sloppy Hash Table）协议</h2>



<p class="wp-block-paragraph">BitTorrent 网络本身是一个容错性十分高的网络，但其一大弱点就是 Tracker 服务器，因为一旦 Tracker 服务器下线，网络中的主机就无法联系到持有所需资源的主机。</p>



<p class="wp-block-paragraph">分布式散列表协议也称 DHT 协议，它可以让 BitTorrent 网络中的主机在不依赖 Tracker 服务器的情况下联系到持有所需资源的主机。</p>



<h2 class="wp-block-heading">伙伴交换（Peer Exchange）协议</h2>



<p class="wp-block-paragraph">伙伴交换（Peer Exchange）协议是让两个 Peer 互相交换对方的 Peer 的协议。其实就是两个朋友之间互相向对方介绍自己的朋友，这样大家的朋友就会越来越多。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.addesp.com/archives/5236/feed</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Windows10 蓝牙开关消失</title>
		<link>https://www.addesp.com/archives/5010</link>
					<comments>https://www.addesp.com/archives/5010#comments</comments>
		
		<dc:creator><![CDATA[ADD-SP]]></dc:creator>
		<pubDate>Tue, 10 Aug 2021 16:06:12 +0000</pubDate>
				<category><![CDATA[笔记]]></category>
		<category><![CDATA[Windows]]></category>
		<guid isPermaLink="false">https://www.addesp.com/?p=5010</guid>

					<description><![CDATA[你可能会发现 Windows10 的蓝牙开关突然消失不见了，并且不能连上蓝牙。 你可能已经试过网上的方法，比如 [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">你可能会发现 Windows10 的蓝牙开关突然消失不见了，并且不能连上蓝牙。</p>



<p class="wp-block-paragraph">你可能已经试过网上的方法，比如重新安装蓝牙驱动，去寻找「设备管理器」中寻找不存在的带有感叹号的 USB 设备，去启动 「蓝牙支持服务」和调整 BIOS 的设置等。</p>



<p class="wp-block-paragraph">如果这些方法都没用不如来试试下面这个方法。</p>



<p class="wp-block-paragraph"> </p>



<figure class="wp-block-image size-full"><img fetchpriority="high" decoding="async" width="720" height="311" src="https://www.addesp.com/wp-content/uploads/2021/08/A4ECF8F69E4EC4EE5241FF990677472614D.webp" alt="" class="wp-image-5011"/><figcaption><a href="https://tieba.baidu.com/p/5771379585" target="_blank" rel="noreferrer noopener">【垃圾y7000】y7000蓝牙神秘失踪为哪般？打鸡血修复【笔记本吧】_百度贴吧 (baidu.com)</a></figcaption></figure>



<p class="wp-block-paragraph">虽然我不知道原理是什么，但是确实有用。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.addesp.com/archives/5010/feed</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Nextcloud 423 Locked</title>
		<link>https://www.addesp.com/archives/4589</link>
					<comments>https://www.addesp.com/archives/4589#comments</comments>
		
		<dc:creator><![CDATA[ADD-SP]]></dc:creator>
		<pubDate>Sat, 05 Jun 2021 04:07:42 +0000</pubDate>
				<category><![CDATA[Nextcloud]]></category>
		<category><![CDATA[故障排除]]></category>
		<guid isPermaLink="false">https://www.addesp.com/?p=4589</guid>

					<description><![CDATA[概述 Nextcloud 中对文件进行修改、删除操作会失败。网页端显示错误，客户端显示返回 HTTP 423  [&#8230;]]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">概述</h2>



<p class="wp-block-paragraph">Nextcloud 中对文件进行修改、删除操作会失败。网页端显示错误，客户端显示返回 HTTP 423 (Locked) 状态码。</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"><p>HTTP 423 状态码并未定义在 HTTP 标准中。其定义在 <a href="https://datatracker.ietf.org/doc/html/rfc4918#page-78" target="_blank" rel="noreferrer noopener" title="https://datatracker.ietf.org/doc/html/rfc4918#page-78">RFC 4918</a> 中作为 HTTP 协议的一个扩展。</p><cite><a href="https://datatracker.ietf.org/doc/html/rfc4918" target="_blank" rel="noreferrer noopener" title="https://datatracker.ietf.org/doc/html/rfc4918">HTTP Extensions for Web Distributed Authoring and Versioning (WebDAV)  &#8212; RFC 4918</a></cite></blockquote>



<h2 class="wp-block-heading">系统环境</h2>



<h3 class="wp-block-heading">宿主机</h3>



<ul class="wp-block-list"><li>OS：Ubuntu 20.04</li><li>Server：nginx-1.21.0</li><li>MySQL：8.0.25</li><li>Nextcloud：21.0.0</li></ul>



<h3 class="wp-block-heading">Docker</h3>



<ul class="wp-block-list"><li>php:8.07-fpm-alpine3.13</li></ul>



<h2 class="wp-block-heading">解决方法</h2>



<h3 class="wp-block-heading">临时解决</h3>



<ol class="wp-block-list"><li>开启维护模式，详见<a href="https://docs.nextcloud.com/server/latest/admin_manual/maintenance/backup.html" target="_blank" rel="noreferrer noopener" title="https://docs.nextcloud.com/server/latest/admin_manual/maintenance/backup.html">官方文档</a>。</li><li>清空数据库中的表  <code>oc_file_locks</code>。<strong><span class="has-inline-color has-vivid-red-color">不是删除表！</span></strong></li><li>关闭维护模式。</li><li>确保你的后台任务能够正常执行，后台任务在「设置 -> 基本设置」中查看。</li></ol>



<h3 class="wp-block-heading">一劳永逸</h3>



<ul class="wp-block-list"><li>如果你可以控制 Nextcloud 所在服务器，建议为 Nextcloud 配置 Redis 缓存。配置方法见<a href="https://docs.nextcloud.com/server/latest/admin_manual/configuration_server/caching_configuration.html?highlight=cache" target="_blank" rel="noreferrer noopener" title="https://docs.nextcloud.com/server/latest/admin_manual/configuration_server/caching_configuration.html?highlight=cache">官方文档</a>。</li><li>如果你不能为 Nextcloud 配置 Redis 缓存，你可以关闭文件锁定。修改 <code>config/config.php</code> 文件，写入 <code>'filelocking.enabled' => false</code>。</li></ul>



<p class="wp-block-paragraph">关闭文件锁定可能会导致并发状态下对文件的操作出错，比如造成文件内容混乱或损坏。</p>



<h2 class="wp-block-heading">参考资料</h2>



<ul class="wp-block-list"><li><a href="https://help.nextcloud.com/t/file-is-locked-how-to-unlock/1883" target="_blank" rel="noreferrer noopener" title="https://help.nextcloud.com/t/file-is-locked-how-to-unlock/1883">File is locked &#8211; how to unlock &#8211; <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4d1.png" alt="📑" class="wp-smiley" style="height: 1em; max-height: 1em;" /> How to &#8211; Nextcloud community</a></li></ul>
]]></content:encoded>
					
					<wfw:commentRss>https://www.addesp.com/archives/4589/feed</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		
			</item>
		<item>
		<title>从零设计通信协议（二）</title>
		<link>https://www.addesp.com/archives/4528</link>
					<comments>https://www.addesp.com/archives/4528#comments</comments>
		
		<dc:creator><![CDATA[ADD-SP]]></dc:creator>
		<pubDate>Mon, 24 May 2021 13:21:42 +0000</pubDate>
				<category><![CDATA[从零设计通信协议]]></category>
		<category><![CDATA[唯密文攻击]]></category>
		<category><![CDATA[网络安全]]></category>
		<category><![CDATA[通信协议]]></category>
		<guid isPermaLink="false">https://www.addesp.com/?p=4528</guid>

					<description><![CDATA[升级协议以抵御「唯密文攻击」。]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">写在前面</h2>



<ul class="wp-block-list"><li>本系列的介绍以及协议功能见「<a href="https://www.addesp.com/archives/4463">从零设计通信协议（一）</a>」。</li><li>本系列的全部文章见「<a href="https://www.addesp.com/archives/category/%e7%ac%94%e8%ae%b0/%e4%bb%8e%e9%9b%b6%e8%ae%be%e8%ae%a1%e9%80%9a%e4%bf%a1%e5%8d%8f%e8%ae%ae">从零设计通信协议系列</a>」。</li></ul>



<h2 class="wp-block-heading">上一版协议</h2>



<p class="wp-block-paragraph">首先回顾的以下上一版的协议。</p>



<figure class="wp-block-table aligncenter is-style-regular"><table class="has-fixed-layout"><thead><tr><th class="has-text-align-center" data-align="center">1 字节</th><th class="has-text-align-center" data-align="center">X 字节</th></tr></thead><tbody><tr><td class="has-text-align-center" data-align="center">数据长度</td><td class="has-text-align-center" data-align="center">数据</td></tr></tbody></table><figcaption>数据包格式</figcaption></figure>



<p class="wp-block-paragraph">整个数据包使用 AES-128-ECB 加密。</p>



<ul class="wp-block-list"><li>数据长度：表示「数据」的长度，单位为字节。</li><li>数据：表示具体的通信数据。</li></ul>



<p class="wp-block-paragraph">我们可以通过数据包的前一个字节获取数据长度，之后向后读取 X 字节即可获取全部的数据，整个数据包使用 AES-128-ECB 加密，看起来很安全。其实使用 AES-128-ECB 加密容易受到「唯密文攻击」。</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"><p>唯密文攻击：指在仅知道密文的情况加进行攻击，攻击成功轻则获得部分或全部明文，重则获取密钥。</p><cite><a href="https://baike.baidu.com/item/%E5%94%AF%E5%AF%86%E6%96%87%E6%94%BB%E5%87%BB" target="_blank" rel="noreferrer noopener" title="https://baike.baidu.com/item/%E5%94%AF%E5%AF%86%E6%96%87%E6%94%BB%E5%87%BB">唯密文攻击_百度百科</a></cite></blockquote>



<h2 class="wp-block-heading">ECB 模式的缺陷</h2>



<p class="wp-block-paragraph">ECB 模式的缺陷就是相同的明文会产生相同的密文，这会让攻击者有机会猜出密文的含义，难以抵抗统计分析。</p>



<p class="wp-block-paragraph">例如我和 Alice 对话，每次我发送的第一个信息是「你好」，假设使用 ECB 模式加密后的密文为「#￥%#￥」，那么如果 Eve 中间截获我的数据包，发现我和 Alice 每次通信我都会先发一个「#￥%#￥」给 Alice。根据日常的对话习惯，Eve 猜测这可能是在打招呼，进而怀疑「#￥%#￥」解密后是「你好」、「有空么」之类的话，这已经十分接近我们的明文信息了。</p>



<p class="wp-block-paragraph">上面的例子可能并不特别可怕，但是如果进行统计分析的话就难说了。</p>



<h2 class="wp-block-heading">CBC 模式</h2>



<p class="wp-block-paragraph">CBC 即密码分组链接（<strong>C</strong>ipher-<strong>B</strong>lock <strong>C</strong>haining），我们无需关心原理，只需要知道该模式通过一个随机的初始化向量 <strong>IV</strong> 保证了每条消息的唯一性，即相同的明文可以对应许多个密文。</p>



<h2 class="wp-block-heading">第二版协议</h2>



<p class="wp-block-paragraph">下面我们使用 AES-128-CBC 算法来加密我们的通讯信息，使用 CBC 模式加密不仅需要密钥，也需要一个随机的初始化向量 <strong>IV</strong>，所以我们需要将 <strong>IV</strong> 传递给对方。</p>



<figure class="wp-block-table is-style-regular"><table class="has-fixed-layout"><thead><tr><th class="has-text-align-center" data-align="center">16 字节</th><th class="has-text-align-center" data-align="center">1 字节</th><th class="has-text-align-center" data-align="center">X 字节</th></tr></thead><tbody><tr><td class="has-text-align-center" data-align="center">IV</td><td class="has-text-align-center" data-align="center">数据长度</td><td class="has-text-align-center" data-align="center">数据</td></tr></tbody></table><figcaption>数据包格式</figcaption></figure>



<p class="wp-block-paragraph"><strong>数据包从第 17 个字节开始的所有数据要使用 AES-128-CBC 加密。</strong></p>



<ul class="wp-block-list"><li>IV：AES-128-CBC 加密时所用的初始化向量。</li><li>数据长度：表示「数据」的长度，单位为字节。</li><li>数据：表示具体的通信数据。</li></ul>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"><p>初始化向量 <strong>IV</strong> 可以公开，但要保证不可预测。</p></blockquote>



<h2 class="wp-block-heading">写在最后</h2>



<p class="wp-block-paragraph">本次我们升级了协议，使得协议可以抵御「唯密文攻击」，本系列将会持续完善这一协议。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.addesp.com/archives/4528/feed</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		
			</item>
		<item>
		<title>从零设计通信协议（一）</title>
		<link>https://www.addesp.com/archives/4463</link>
					<comments>https://www.addesp.com/archives/4463#respond</comments>
		
		<dc:creator><![CDATA[ADD-SP]]></dc:creator>
		<pubDate>Mon, 24 May 2021 07:19:41 +0000</pubDate>
				<category><![CDATA[从零设计通信协议]]></category>
		<category><![CDATA[网络安全]]></category>
		<category><![CDATA[通信协议]]></category>
		<guid isPermaLink="false">https://www.addesp.com/?p=4463</guid>

					<description><![CDATA[逐步完善一个玩具性质的应用层通信协议。]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">写在前面</h2>



<p class="wp-block-paragraph">当你看到这个网页时你会知道 HTTPS 协议正在工作，你还知道这个协议能够抵御许多的网络攻击，保证通信安全。不过，我们知道的可能没有更多了。</p>



<p class="wp-block-paragraph">作为一个充满好奇心的沙雕，我希望自己搞一个玩具性质的应用层通信协议。所以本系列将会把我的设计过程记录在这里，本系列每次更新都会进行增强协议功能或修复安全性问题，你或许可以学习到一些知识，或许也可能找出我的一些错误。</p>



<h2 class="wp-block-heading">协议功能</h2>



<p class="wp-block-paragraph">为通信双方提供一个可靠的、有序的、安全的通信协议。要求通信双方预先共享足够强的密钥，并保证密钥不泄露。</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"><p>我们假装 TLS 不存在。</p></blockquote>



<h2 class="wp-block-heading">有序和可靠</h2>



<p class="wp-block-paragraph">既然是应用层协议，那么应用层以下的协议自然是随便选的。TCP 协议就可以保证数据传输的有序和可靠，所以本协议使用 TCP 作为底层传输协议。</p>



<h2 class="wp-block-heading">安全</h2>



<p class="wp-block-paragraph">什么是安全呢？我们就暂时定义为通信内容不被泄露和篡改吧。</p>



<h2 class="wp-block-heading">第一版协议</h2>



<p class="wp-block-paragraph">目前我们的协议很简单，再加上已经预先共享了密钥，所以就先不加上握手机制了。所以现在要做的就是选择加密算法，随便从网上找找，就用 AES-128-ECB 吧。</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"><p>AES-128-ECB：AES 表示高级加密标准（<strong>A</strong>dvanced <strong>E</strong>ncryption <strong>S</strong>tandard）；128 表示密钥长度为 128 bit；ECB 表示加密模式为电子密码本（<strong>E</strong>lectronic <strong>C</strong>ode<strong>B</strong>ook）。</p></blockquote>



<p class="wp-block-paragraph">选择好了加密算法，那么我们就开始设计我们数据包格式吧。</p>



<figure class="wp-block-table aligncenter is-style-regular"><table class="has-fixed-layout"><thead><tr><th class="has-text-align-center" data-align="center">1 字节</th><th class="has-text-align-center" data-align="center">X 字节</th></tr></thead><tbody><tr><td class="has-text-align-center" data-align="center">数据长度</td><td class="has-text-align-center" data-align="center">数据</td></tr></tbody></table><figcaption>数据包格式</figcaption></figure>



<p class="wp-block-paragraph">整个数据包使用 AES-128-ECB 加密。</p>



<ul class="wp-block-list"><li>数据长度：表示「数据」的长度，单位为字节。</li><li>数据：表示具体的通信数据。</li></ul>



<p class="wp-block-paragraph">这样只要任何一方将上面的数据包正确解密就可以获取通信的内容，也可以按照此格式给对方发送信息。</p>



<h2 class="wp-block-heading">写在最后</h2>



<p class="wp-block-paragraph">协议十分简陋，但是已经算是一个雏形了，本系列将会持续完善这一协议。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.addesp.com/archives/4463/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>为什么 JavaScript String 会有 slice() 和 substring() 这两个方法？</title>
		<link>https://www.addesp.com/archives/1902</link>
					<comments>https://www.addesp.com/archives/1902#respond</comments>
		
		<dc:creator><![CDATA[ADD-SP]]></dc:creator>
		<pubDate>Thu, 01 Oct 2020 16:17:57 +0000</pubDate>
				<category><![CDATA[笔记]]></category>
		<category><![CDATA[JavaScript]]></category>
		<guid isPermaLink="false">https://www.addesp.com/?p=1902</guid>

					<description><![CDATA[slice() 和 substring() 这两个方法都可以截取字符串，那么为什么会设计这两个 API 呢？]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">写在前面</h2>



<p class="wp-block-paragraph"><code>slice()</code> 和 <code>substring(</code>) 这两个方法都可以截取字符串，那么为什么会设计这两个 API 呢？</p>



<h2 class="wp-block-heading">二者的区别</h2>



<p class="wp-block-paragraph"><code>substring()</code> 和 <code>slice()</code> 方法几乎是相同的，但两者之间有一些微妙的区别，特别是在处理负参数的方式上。</p>



<p class="wp-block-paragraph">如果 <code>indexStart</code> 大于 <code>indexEnd</code>，<code>substring()</code> 方法会交换两个参数，这意味着仍然会返回一个字符串。但是 <code>slice()</code> 方法面对这种情况会返回一个空字符串。</p>



<pre class="wp-block-code"><code>let text = 'Mozilla'
console.log(text.substring(5, 2))  // => "zil" 
console.log(text.slice(5, 2))      // => ""</code></pre>



<p class="wp-block-paragraph">如果参数中的一个或两个都是负数或 <code>NaN</code>，则 <code>substring()</code> 将它们当作 0 处理。</p>



<pre class="wp-block-code"><code>console.log(text.substring(-5, 2))  // => "Mo"
console.log(text.substring(-5, -2)) // => "" </code></pre>



<p class="wp-block-paragraph"><code>slice()</code> 也将 <code>NaN</code> 参数视为 0，但当它为负时，它会从字符串的末尾向前数，以找到索引。</p>



<pre class="wp-block-code"><code>console.log(text.slice(-5, 2))   // => ""
console.log(text.slice(-5, -2))  // => "zil"</code></pre>



<h2 class="wp-block-heading">共存的原因</h2>



<p class="wp-block-paragraph">乍一看 <code>slice()</code> 和 <code>substring()</code> 做的是同样的事情，但最大的区别在于它们如何处理负参数。</p>



<p class="wp-block-paragraph">当 Netscape 2.0 首次实现 JavaScript 时，只有一个 <code>substring()</code> 方法。如果它的任何一个参数是负数，就会被当作 0 处理。</p>



<p class="wp-block-paragraph">当 Netscape 4.0 引入 JavaScript 1.2 后，他们希望通过支持负索引来从反向遍历字符串。不过不能修改 <code>substring()</code> 的行为 ，因为这会破坏现有的代码的兼容性，所以只能创建一个新的函数来支持新增的功能。这个函数就是 <code>slice()</code>，在 <code>Array</code> 和 <code>String</code> 上都实现了。</p>



<h2 class="wp-block-heading">参考资料</h2>



<ul class="wp-block-list"><li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/substring" target="_blank" rel="noreferrer noopener">String.prototype.substring() &#8211; JavaScript | MDN</a></li><li><a href="https://stackoverflow.com/questions/18224226/why-two-different-methods-slice-substring" target="_blank" rel="noreferrer noopener">javascript &#8211; Why two different methods slice() &amp; substring()? &#8211; Stack Overflow</a></li><li><a href="https://www.arungudelli.com/tutorial/javascript/javascript-string-substring-vs-string-substr-vs-string-slice-differences/" target="_blank" rel="noreferrer noopener">JavaScript Substring Vs Substr Vs Slice Differences With Examples | Arunkumar Blog</a></li></ul>
]]></content:encoded>
					
					<wfw:commentRss>https://www.addesp.com/archives/1902/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>shell 脚本入门笔记</title>
		<link>https://www.addesp.com/archives/927</link>
					<comments>https://www.addesp.com/archives/927#respond</comments>
		
		<dc:creator><![CDATA[ADD-SP]]></dc:creator>
		<pubDate>Fri, 11 Sep 2020 20:28:05 +0000</pubDate>
				<category><![CDATA[笔记]]></category>
		<category><![CDATA[shell]]></category>
		<guid isPermaLink="false">https://www.addesp.com/?p=927</guid>

					<description><![CDATA[被迫学习 shell]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">基本格式</h2>



<pre class="wp-block-code language-shell"><code>#!/bin/bash
echo 'Hello World'</code></pre>



<p class="wp-block-paragraph"><code>#!/bin/bash</code>：脚本的第一行声明了脚本的解释器，对于 shell 脚本解释器通常为 <code>/bin/bash</code>，当然如果是 python 脚本也可以写成 <code>#!/bin/python</code>。</p>



<p class="wp-block-paragraph">第一行之后的所有内容就是具体的命令，变量以及函数了。</p>



<h2 class="wp-block-heading">运行方式</h2>



<pre class="wp-block-code language-shell"><code># 使用脚本中声明的解释器解释执行，如果未声明则使用当前 shell 解释执行。
./script.sh

# 使用指定的解释器执行
# 使用 /bin/sh 解释执行
sh script.sh
# 使用 /bin/bash 解释执行
bash script.sh</code></pre>



<h2 class="wp-block-heading">变量</h2>



<h3 class="wp-block-heading">永久环境变量</h3>



<p class="wp-block-paragraph">本质属于文件，对于所有 shell 均可访问。</p>



<h3 class="wp-block-heading">临时环境变量</h3>



<p class="wp-block-paragraph">只允许当前 shell 以及其子 shell 访问。一般通过 <code>export</code> 来设置。</p>



<pre class="wp-block-code language-shell"><code>export VAR_NAME=alice</code></pre>



<h3 class="wp-block-heading">临时变量</h3>



<p class="wp-block-paragraph">仅仅允许当前 shell 访问。直接在声明即可。shell 脚本中不存在高级语言中局部变量这个概念，也就是 shell 脚本中任何位置声明的变量均为全局变量。</p>



<pre class="wp-block-code language-shell"><code>var1=1</code></pre>



<h3 class="wp-block-heading">特殊变量</h3>



<figure class="wp-block-table"><table><tbody><tr><td>变量</td><td>含义</td></tr><tr><td>$0</td><td>当前脚本的文件名</td></tr><tr><td>$n</td><td>传递给脚本或函数的参数。n 是一个数字，表示第几个参数。例如，第一个参数是$1，第二个参数是$2。</td></tr><tr><td>$#</td><td>传递给脚本或函数的参数个数。</td></tr><tr><td>$*<br></td><td>传递给脚本或函数的所有参数。</td></tr><tr><td>$@</td><td>传递给脚本或函数的所有参数。被双引号(&#8221; &#8220;)包含时，与 $* 稍有不同，下面将会讲到。</td></tr><tr><td>$?<br></td><td>上个命令的退出状态，或函数的返回值。</td></tr><tr><td>$$</td><td>当前Shell进程ID。对于 Shell 脚本，就是这些脚本所在的进程ID。</td></tr></tbody></table></figure>



<h4 class="wp-block-heading">\$* 和 \$@</h4>



<p class="wp-block-paragraph">这两个变量在不使用双引号包裹的时候完全一样</p>



<pre class="wp-block-code language-shell"><code>#!/bin/bash
function foo()
{
    for var in $*
    do
        # -e 表示启用转义字符
        echo -e "$var\n"
    done
}

foo 1 2 3 4 5</code></pre>



<pre class="wp-block-code language-shell"><code>#!/bin/bash
function foo()
{
    for var in $@
    do
        # -e 表示启用转义字符
        echo -e "$var\n"
    done
}

foo 1 2 3 4 5</code></pre>



<p class="wp-block-paragraph">上面两个脚本的执行结果一样：</p>



<pre class="wp-block-preformatted">1
2
3
4
5</pre>



<p class="wp-block-paragraph">但是一旦被双引号包裹就不一样了</p>



<pre class="wp-block-code language-shell"><code>#!/bin/bash
function foo()
{
    for var in "$*"
    do
        # -e 表示启用转义字符
        echo -e "$var\n"
    done
}

foo 1 2 3 4 5</code></pre>



<p class="wp-block-paragraph">输出为 <code>1 2 3 4 5</code>[换行]</p>



<pre class="wp-block-code language-shell"><code>#!/bin/bash
function foo()
{
    for var in $@
    do
        # -e 表示启用转义字符
        echo -e "$var\n"
    done
}

foo 1 2 3 4 5</code></pre>



<p class="wp-block-paragraph">输出结果依旧为</p>



<pre class="wp-block-preformatted">1
2
3
4
5</pre>



<h2 class="wp-block-heading">基础算数运算</h2>



<p class="wp-block-paragraph">可以进行简单的四则运算。但是 bash 不支持浮点运算，所以除法会直接截断小鼠部分。同时由于 <code>*</code> 在 shell 中有特殊的含义，所以必须使用 <code>\</code> 进行转义才能使用乘法。</p>



<pre class="wp-block-code language-shell"><code># 结果为 3
expr 1 + 2

# 结果为 -1
expr 1 - 2

# 结果为 6
expr 2 \* 3

# 结果为 2
expr 5 / 2</code></pre>



<h2 class="wp-block-heading">条件判断</h2>



<p class="wp-block-paragraph">Shell中的 <code>test </code>命令用于检查某个条件是否成立，它可以进行数值、字符和文件三个方面的测试。 <code>test</code> 可以用 <code>[]</code> 代替，如 <code>test $var1 -eq $var2</code> 与 <code>[ $var1 -eq $var2 ]</code> 等价。但是不允许省略空格，也就是说 <code>[$var1 -eq $var2]</code> 是错的。</p>



<h3 class="wp-block-heading">数字比较</h3>



<figure class="wp-block-table is-style-regular"><table><tbody><tr><td>参数</td><td>作用</td><td>举例</td></tr><tr><td>-eq</td><td>相等则为真</td><td><code>test $var1 -eq $var2</code></td></tr><tr><td>-ne</td><td>不等则为假</td><td><code>test $var1 -ne $var2</code></td></tr><tr><td>-gt</td><td>大于则为真</td><td><code>test $var1 -gt $var2</code></td></tr><tr><td>-ge</td><td>大于等于则为真</td><td><code>test $var1 -ge $var2</code></td></tr><tr><td>-lt</td><td>小于则为真</td><td><code>test $var1 -lt $var2</code></td></tr><tr><td>-le</td><td>小于等于则为真</td><td><code>test $var1 -le $var2</code></td></tr></tbody></table><figcaption>数字比较</figcaption></figure>



<h3 class="wp-block-heading">字符串比较</h3>



<figure class="wp-block-table"><table><tbody><tr><td>参数</td><td>作用</td><td>举例</td></tr><tr><td>=</td><td>等于则为真</td><td><code>test $str1 = $str2</code></td></tr><tr><td>!=</td><td>不等则为真</td><td><code>test $str1 != $str2</code></td></tr><tr><td>-z</td><td>字符串长度为零则为真</td><td><code>test -z $str1</code></td></tr><tr><td>-n</td><td>字符串长度不为零则为真</td><td><code>test -n $str1</code></td></tr></tbody></table><figcaption>字符串比较</figcaption></figure>



<h3 class="wp-block-heading">文件判断</h3>



<figure class="wp-block-table"><table><tbody><tr><td>参数</td><td>作用</td><td>举例</td></tr><tr><td>-e</td><td>如果文件存在则为真</td><td><code>test -e $filename</code></td></tr><tr><td>-r</td><td>如果文件存在且可读则为真</td><td><code>test -r $filename</code></td></tr><tr><td>-w</td><td>如果文件存在且可写则为真</td><td><code>test -w $filename</code></td></tr><tr><td>-x</td><td>如果文件存在且可执行则为真</td><td><code>test -x $filename</code></td></tr><tr><td>-s</td><td>如果文件存在且至少有一个字符则为真</td><td><code>test -s $filename</code></td></tr><tr><td>-d</td><td>如果文件存在且为目录则为真</td><td><code>test -d $filename</code></td></tr><tr><td>-f</td><td>如果文件存在且为普通文件则为真</td><td><code>test -f $filename</code></td></tr><tr><td>-c</td><td>如果文件存在且为字符型特殊文件则为真</td><td><code>test -c $filename</code></td></tr><tr><td>-b</td><td>如果文件存在且为块特殊文件则为真</td><td><code>test -b $filename</code></td></tr></tbody></table><figcaption>文件判断</figcaption></figure>



<h2 class="wp-block-heading">流程控制</h2>



<h3 class="wp-block-heading">if</h3>



<h4 class="wp-block-heading">语法</h4>



<pre class="wp-block-code language-shell"><code>if boolean ; then
    # TODO
fi

if boolean
then
   # TODO
fi</code></pre>



<h4 class="wp-block-heading">举例</h4>



<p class="wp-block-paragraph">shell 代码</p>



<pre class="wp-block-code language-shell"><code>#!/bin/bash
if &#91; 1 -eq 2 ] ; then
    echo '1 == 2'</code></pre>



<p class="wp-block-paragraph">等价 C 代码</p>



<pre class="wp-block-code language-shell"><code>if (1 == 2) {
    printf("1 == 2");
}</code></pre>



<h3 class="wp-block-heading">if···else</h3>



<h4 class="wp-block-heading">语法</h4>



<pre class="wp-block-code language-shell"><code>if boolean ; then
    # TODO
else
    # TODO
fi

if boolean
then
    # TODO
else
    # TODO
fi</code></pre>



<h4 class="wp-block-heading">举例</h4>



<p class="wp-block-paragraph">shell 代码</p>



<pre class="wp-block-code language-shell"><code>#!/bin/bash
if &#91; 1 -eq 2 ] ; then
    echo '1 == 2'
else
    echo '1 != 2'
fi</code></pre>



<p class="wp-block-paragraph">等价 C 代码</p>



<pre class="wp-block-code language-shell"><code>if (1 == 2) {
    printf("1 == 2");
} else {
    printf("1 != 2");
}</code></pre>



<h3 class="wp-block-heading">if···elif</h3>



<pre class="wp-block-code language-shell"><code>if boolean ; then
    # TODO
elif boolean ; then
    # TODO
fi

if boolean
then
    # TODO
elif boolean
then
    # TODO
fi</code></pre>



<h3 class="wp-block-heading">for</h3>



<h4 class="wp-block-heading">语法</h4>



<pre class="wp-block-code language-shell"><code>for var in item0 item1 item2 ....
do
    # TODO
done</code></pre>



<h4 class="wp-block-heading">举例</h4>



<pre class="wp-block-code language-shell"><code>#!/bin/bash
for num in 1 2 3 4 5
do
    # -e 代表开启字符转义
    echo -e "${num}\n"
done </code></pre>



<p class="wp-block-paragraph">结果</p>



<pre class="wp-block-preformatted">1
2
3
4
5</pre>



<h3 class="wp-block-heading">while</h3>



<h4 class="wp-block-heading">语法</h4>



<pre class="wp-block-code language-shell"><code>while boolean
do
    # TODO
done</code></pre>



<h4 class="wp-block-heading">举例</h4>



<p class="wp-block-paragraph">shell 代码</p>



<pre class="wp-block-code language-shell"><code>#!/bin/bash
max=6
num=0
while &#91; $num -lt $max ]
do
    echo -e "${num}\n"
    num=`expr $num + 1`
done</code></pre>



<p class="wp-block-paragraph">等价 C 代码</p>



<pre class="wp-block-code language-shell"><code>int max = 6, num = 0;
while (num &lt; max) {
    printf("%d\n", num);
    num += 1;
}</code></pre>



<h2 class="wp-block-heading">函数</h2>



<h3 class="wp-block-heading">语法</h3>



<pre class="wp-block-code language-shell"><code># 声明函数（无论有无参数括号内均为空）
function name()
{
    # TODO
    return value
}

# 调用参数，后面跟上若干的参数
name 1 2 3 ...</code></pre>



<p class="wp-block-paragraph">其中 function 可以省略，return 也可以省略，返回值默认为最后一条指令的执行结果，return 只允许接数字，且范围为$[0,255]$。</p>



<h3 class="wp-block-heading">举例</h3>



<pre class="wp-block-code language-shell"><code>#!/bin/bash

function sum()
{
    ret=0
    for num in $@
    do
            ret=`expr $ret + $num`
    done
    return $ret
}

# 计算 1 + 2 + 3
sum 1 2 3
echo $?</code></pre>



<h2 class="wp-block-heading">双引号、单引号和反引号</h2>



<h3 class="wp-block-heading">双引号</h3>



<p class="wp-block-paragraph">双引号中允许使用变量。</p>



<pre class="wp-block-code language-shell"><code>#!/bin/bash
str=World
echo "Hello $str"</code></pre>



<p class="wp-block-paragraph">结果为 Hello World。</p>



<h3 class="wp-block-heading">单引号</h3>



<p class="wp-block-paragraph">单引号中的内容将原文输出，但是不对转义符生效。</p>



<pre class="wp-block-code language-shell"><code>#!/bin/bash
str=World
echo 'Hello $str'</code></pre>



<p class="wp-block-paragraph">结果为 Hello $str。</p>



<h3 class="wp-block-heading">反引号</h3>



<p class="wp-block-paragraph">会将反引号中的命令作为返回值</p>



<pre class="wp-block-code language-shell"><code>#!/bin/bash
list=`ls`
echo $list</code></pre>



<p class="wp-block-paragraph">结果是当前目录中的文件。</p>



<h2 class="wp-block-heading">$()</h2>



<p class="wp-block-paragraph"><code>$()</code> 类似函数一样让我们将某个命令赋值给某个变量，当使用该变量时才会执行对应的命令并将返回值作为变量的值。</p>



<h2 class="wp-block-heading">(())</h2>



<p class="wp-block-paragraph">扩展运算，支持了 C 语言大部分的写法。</p>



<ul class="wp-block-list"><li>在双括号结构中，所有表达式可以像c语言一样，如：<code>a++</code>，<code>b--</code> 等。</li><li>在双括号结构中，所有变量可以不加入：<code>$</code> 符号前缀。</li><li>双括号可以进行逻辑运算，四则运算。</li><li>双括号结构 扩展了 <code>for</code>，<code>while</code>, <code>if</code> 条件测试运算</li><li>支持多个表达式运算，各个表达式之间用 <code>,</code> 分开。</li></ul>



<pre class="wp-block-code language-shell"><code>#!/bin/bash
num=1
((num++))
((num=num+1+2-3*4/5))
((num+=1+2-3*4/5))

# 打印 &#91;0...9]
for((i=0;i&lt;10;i++))
do
    echo -e "$((i))\n"
done

# 如果 5 能被 2 整除则输出 true，反之输出 false
if ((5%2==0)) ; then
    echo 'true'
else
    echo 'false'
</code></pre>
]]></content:encoded>
					
					<wfw:commentRss>https://www.addesp.com/archives/927/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>mysql 问题及解决方案汇总</title>
		<link>https://www.addesp.com/archives/745</link>
					<comments>https://www.addesp.com/archives/745#respond</comments>
		
		<dc:creator><![CDATA[ADD-SP]]></dc:creator>
		<pubDate>Wed, 09 Sep 2020 07:32:09 +0000</pubDate>
				<category><![CDATA[笔记]]></category>
		<category><![CDATA[mysql]]></category>
		<guid isPermaLink="false">https://www.addesp.com/?p=745</guid>

					<description><![CDATA[Mysql 相关的问题症状原因和解决方法]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">mysql ERROR 1051</h2>



<h3 class="wp-block-heading">症状</h3>



<p class="wp-block-paragraph">mysql 版本：8.0.21</p>



<p class="wp-block-paragraph">执行删除表命令</p>



<pre class="wp-block-code"><code>drop table tbl_name;</code></pre>



<p class="wp-block-paragraph">会报错<code>ERROR 1051 (42S02): Unknown table 'demo_db.tbl_name'</code>。</p>



<p class="wp-block-paragraph"></p>



<h3 class="wp-block-heading">原因</h3>



<p class="wp-block-paragraph">如果不是两次安装 mysql 导致 store engine&nbsp; 不同的话可能是因为 tbl_name 不是一个 table，而是一个 view。</p>



<p class="wp-block-paragraph">平常我们使用下面的命令来列出所有表</p>



<pre class="wp-block-code"><code>show tables;</code></pre>



<pre class="wp-block-preformatted">+-------------------+
| Tables_in_demo_db |
+-------------------+
| demo_table        |
| demo_view         |
+-------------------+</pre>



<p class="wp-block-paragraph">但是容易被忽略的一点是该命令会连带 view 一起列出来。为了只列出 table，可以使用下面这条命令。</p>



<pre class="wp-block-code"><code>show full tables;</code></pre>



<pre class="wp-block-preformatted">+-------------------+------------+
| Tables_in_demo_db | Table_type |
+-------------------+------------+
| demo_table        | BASE TABLE |
| demo_view         | VIEW       |
+-------------------+------------+</pre>



<p class="wp-block-paragraph">这样就可以明确知道其类型是 table 还是 view。</p>



<h3 class="wp-block-heading">解决</h3>



<p class="wp-block-paragraph">对于 view 和 table 使用不同的语句进行删除。</p>



<h2 class="wp-block-heading">服务启动失败</h2>



<h3 class="wp-block-heading">情况一</h3>



<h4 class="wp-block-heading">症状</h4>



<p class="wp-block-paragraph">mysql 版本：8.0.21</p>



<p class="wp-block-paragraph">使用 <code>systemctl start mysql</code> 显示</p>



<pre class="wp-block-preformatted">-- Subject: Unit mysql.service has begun start-up
-- Defined-By: systemd
-- Support: http://www.ubuntu.com/support
-- Unit mysql.service has begun starting up.
Oct 16 16:36:45 VM-0-11-ubuntu audit[7397]: AVC apparmor="STATUS" operation="profile_replace" info="same as current prof
Oct 16 16:36:45 VM-0-11-ubuntu kernel: audit: type=1400 audit(1602837405.154:33): apparmor="STATUS" operation="profile_r
Oct 16 16:36:45 VM-0-11-ubuntu systemd[1]: mysql.service: Main process exited, code=exited, status=1/FAILURE
Oct 16 16:36:45 VM-0-11-ubuntu systemd[1]: mysql.service: Failed with result 'exit-code'.
Oct 16 16:36:45 VM-0-11-ubuntu systemd[1]: Failed to start MySQL Community Server.
-- Subject: Unit mysql.service has failed
-- Defined-By: systemd
-- Support: http://www.ubuntu.com/support
-- Unit mysql.service has failed.</pre>



<p class="wp-block-paragraph">查看 mysql 错误日志 <code>cat /var/log/mysql/error.log</code></p>



<pre id="block-9aeb79d7-d9fd-429e-8d93-2c61a1e0bcb8" class="wp-block-preformatted">mysqld: File './binlog.index' not found (OS errno 13 - Permission denied)</pre>



<h4 class="wp-block-heading">原因</h4>



<p class="wp-block-paragraph">mysql 目录的所属用户和用户组不是 mysql。</p>



<h4 class="wp-block-heading">解决</h4>



<p class="wp-block-paragraph">将配置文件中所展现出现的目录如 <code>/var/lib/mysql</code> 的所属用户和用户组改为 mysql。</p>



<h4 class="wp-block-heading">参考</h4>



<ul class="wp-block-list"><li><a href="https://blog.csdn.net/albertjone/article/details/104699080" target="_blank" rel="noreferrer noopener">mysqld: File &#8216;./binlog.index&#8217; not found (OS errno 13 &#8211; Permission denied)_albertjone的博客-CSDN博客</a></li></ul>
]]></content:encoded>
					
					<wfw:commentRss>https://www.addesp.com/archives/745/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>通俗易懂地解释什么是区块链</title>
		<link>https://www.addesp.com/archives/306</link>
					<comments>https://www.addesp.com/archives/306#comments</comments>
		
		<dc:creator><![CDATA[ADD-SP]]></dc:creator>
		<pubDate>Thu, 23 Jul 2020 06:00:00 +0000</pubDate>
				<category><![CDATA[区块链]]></category>
		<category><![CDATA[科普]]></category>
		<category><![CDATA[随笔]]></category>
		<guid isPermaLink="false">http://69a274df.addesp.com/?p=306</guid>

					<description><![CDATA[不讨论细节，旨在勾勒出区块链的轮廓。]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">前言</h2>



<p class="wp-block-paragraph">不讨论细节，旨在勾勒出区块链的轮廓。由于是以介绍为主，所以不会涉及细节，而且<strong>**肯定会有一些错误**</strong>。如果有兴趣的话可以自行查询资料。</p>



<h2 class="wp-block-heading">故事</h2>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"><p>我们先讲个故事：张三是一个三年级的学生，他的父母是做生意的。有一天管帐本的妈妈把账本弄丢了，付出了很大的代价才搞清楚资金的来龙去脉，于是父母商量了一下决定，爸爸和张三每次收支都要告诉妈妈，由妈妈记录这一笔帐，并每天晚上将账本公布，账本开头写上昨日三人的期初余额，结尾写上三人的期末余额。爸爸和张三检查无误后各自抄走一份，如果公布的账本有误则认为是假账，则选择昨日公认的账本作为今天的账本，今日的所有支出作废，损失由妈妈承担。</p></blockquote>



<p class="wp-block-paragraph">检查账本时都要检查下列项目：</p>



<ul class="wp-block-list"><li>每笔交易是否有资金支出者的签名</li><li>资金支出者是否有足够的钱支付</li><li>今日的收支是否计算得出账本结尾声明的三人的期末余额</li><li>今日账本三人的期初余额和昨日账本三人的期末余额一致</li></ul>



<p class="wp-block-paragraph">Q：妈妈是否可以记录一些虚假的交易，比如妈妈擅自记录一笔爸爸转给妈妈&nbsp;100&nbsp;块钱的账？</p>



<p class="wp-block-paragraph">A：不可能，因为这笔转账必须由爸爸的签名才行，如果没有签名则晚上爸爸和张三检查账本的时候会被认为是假账，而损失却由妈妈自己承担。</p>



<p class="wp-block-paragraph">Q：张三是否可以假装爸爸发给妈妈一笔给自己买零食的交替？</p>



<p class="wp-block-paragraph">A：不能，因为没有爸爸的签名。</p>



<hr class="wp-block-separator"/>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"><p>时间一长，妈妈发现记账很累，于是要求每记一笔账都要求支出放支付一块钱给妈妈。后续爸爸和张三发现妈妈记账很赚钱，于是想要自己来记账。一家人商量后达成一致，每个人都将自己的交易发送给其它两人，到了晚上汇总的时候，通过抽签选择采用谁的账本，并给予记账人报酬</p></blockquote>



<hr class="wp-block-separator"/>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"><p>有一次爸爸妈妈一起出差了，抽签抽到爸爸记账，但是爸爸很忙，没时间把账本给张三看，导致张三无法抄写今天的账本。后来一家人商量后达成一致，当允许抄写记账人以外的账本。比如爸爸很忙，但是妈妈有空，妈妈抄完后可以让张三抄。</p></blockquote>



<h2 class="wp-block-heading">解释</h2>



<p class="wp-block-paragraph">这个故事描述了区块链的大致工作流程和特点。<br>区块链中的就是故事中的账本，每天产生一个账本就是每天产生一个「区块」。「链」对应的就是昨日账本的期末余额和今日账本的期初余额，两种余额将两个账本连接在一起，对应将「区块」连接依赖的「链」。</p>



<h3 class="wp-block-heading">区块链的特点</h3>



<ul class="wp-block-list"><li>分布式：对应故事中账本并不是只有记账人有，其它人也有一份。对应现实中数据不只有一份，而是有很多份，在不同的地方存储。</li><li>去中心化：对应故事中对账本起决定作用的人不是记账人，只有一家三口都认同的账本才是有效账本。对应现实中一份数据的内容究竟是什么不是一个人或者一个组织能够决定的，而是要大多数参与者达成共识才能决定。</li><li>点对点：对应故事中抄写账本时可以从任何持有账本的人手中抄写一份，而不是必须从记账人手中抄写。对应现实中一个节点请求数组不是请求中心节点，而是请求任意一个节点即可。</li><li>难以篡改：对应故事中任何人都无法伪造账本中的交易记录，因为必须要有签名。对应现实中依靠密码学保证的防篡改功能。</li></ul>



<h3 class="wp-block-heading">区块链的工作流程</h3>



<ol class="wp-block-list"><li>利用共识选择出一定时间段内的数据的管理员：对应故事中一家三口通过抽签决定记账人。</li><li>当数据发生改动时要对所有的节点进行广播：对应故事中一家三口在晚上决定记账人钱必须将自己的交易告知其余两人。</li><li>对于数据管理员公布的数据要进行检查：对应故事中要对账本进行的四种检查。</li><li>如果检查错误则回滚数据：对应故事中如果检查有误，则选择昨日公认的账本作为今天的账本，今日的所有支出作废。</li><li>如果检查无误则复制一份保存在本地：对应故事中没人都会抄写一份账本。</li></ol>



<h2 class="wp-block-heading">写在最后</h2>



<p class="wp-block-paragraph">本文是指介绍了一下区块链的特点时大致工作流程，并未涉及细节问题，如果有兴趣的话可以自行查询资料。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.addesp.com/archives/306/feed</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>以太坊的智能合约</title>
		<link>https://www.addesp.com/archives/179</link>
					<comments>https://www.addesp.com/archives/179#respond</comments>
		
		<dc:creator><![CDATA[ADD-SP]]></dc:creator>
		<pubDate>Fri, 10 Jul 2020 14:42:43 +0000</pubDate>
				<category><![CDATA[以太坊]]></category>
		<guid isPermaLink="false">http://69a274df.addesp.com/?p=179</guid>

					<description><![CDATA[BTC 实现了去中心化的货币，而 ETH 则实现了去中心化的合约，即智能合约。]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">概述</h2>



<p class="wp-block-paragraph">现实中合约的有效性通常是通过法律来保证的，当合约出现纠纷，或者一方违约，可以通过法律手段解决，如起诉违约人等。<br>BTC 实现了去中心化的货币，而 ETH 则实现了去中心化的合约，即智能合约。<br>智能合约是存储在区块链上的一段代码，可以被调用，代码一般使用 Solidity 语言编写，运行在 EVM（Ethereum Virtual Machine）上，是图灵完备的语言，且代码一旦上链，则极难更改，这有效地保证了合约的有效性，但是如果合约代码有漏洞，也极难有修复，ETH 社区就曾经因为这个出现了 Hard Fork。<br>智能合约对帐户的更改，形成的交易和收据会存储在状态树、交易树和收据树中，并最终通过将三棵树的根哈希值写入 block header 来与其它全节点达成共识。<br>将介绍下列内容：</p>



<ul class="wp-block-list"><li>合约账户</li><li>嵌套调用</li><li>汽油费</li><li>其它特性</li></ul>



<h2 class="wp-block-heading">合约账户</h2>



<p class="wp-block-paragraph">智能合约的账户保存了合约的当前状态：</p>



<ul class="wp-block-list"><li>balance：余额</li><li>nonce：交易次数</li><li>code：合约代码</li><li>storage：存储，数据结构是一颗&nbsp;MPT。</li></ul>



<h2 class="wp-block-heading">嵌套调用</h2>



<ul class="wp-block-list"><li>智能合约的执行具有原子性，执行过程中出现错误会导致回滚。</li><li>嵌套调用是指一个合约调用另一个合约中的函数。</li><li>嵌套调用有时会触发连锁式的回滚，有时则不会。</li></ul>



<h2 class="wp-block-heading">汽油费</h2>



<ul class="wp-block-list"><li>每条语句会消耗一定汽油费（gas）。</li><li>复杂的指令消耗的多，简单的指令消耗少，也有一些是免费的。</li><li>汽油费由发起调用的外部账户一次性支付，如果没有用完则退还。</li><li>如果汽油费耗尽但是合约未执行完毕，则回滚更改，但是不退汽油费。</li><li>最终获取记账权的矿工会获得执行合约的汽油费。</li><li>执行发生错误的交易依然要支付汽油费。</li></ul>



<hr class="wp-block-separator"/>



<p class="wp-block-paragraph">Q：为什么汽油费不足时不退还？</p>



<p class="wp-block-paragraph">A：为了避免恶意的合约调用，如果退还的话，我就可以发布一个合约，里面是死循环代码，然后一直调用这个合约让矿工白白浪费算力，而我却几乎没有任何损失。</p>



<hr class="wp-block-separator"/>



<p class="wp-block-paragraph">Q：为什么执行发生错误的交易依然要支付汽油费？</p>



<p class="wp-block-paragraph">A：原因同上，主要是为了反之恶意的合约和恶意的调用。</p>



<hr class="wp-block-separator"/>



<p class="wp-block-paragraph">Q：每个区块内部的合约调用消耗的总汽油费是否有限制？</p>



<p class="wp-block-paragraph">A：有限制，记录在`header.gasLimit`字段，每个矿工在决定了要发布的区块时可以微调这个值，上调或下调$\frac{1}{1024}$。</p>



<h2 class="wp-block-heading">其它特性</h2>



<ul class="wp-block-list"><li>一个合约内的函数可以调用另一个合约内的函数。</li><li>一个外部账户也可以调用一个合约内的函数。</li><li>合约代码不支持<code>try....catch</code>，若执行时出现异常会触发回滚。</li><li>代码禁止 syscall。</li><li>合约代码只有被外部账户或者其它合约调用时才会执行，无法实现如定时执行等功能。</li><li>合约代码不允许多线程。</li></ul>



<hr class="wp-block-separator"/>



<p class="wp-block-paragraph">Q：为什么禁止 syscall？</p>



<p class="wp-block-paragraph">A：因为执行代码的环境不同，syscall 返回的结果也可能不同，容易导致最终的状态不一致，不同全节点之间难以达成共识。</p>



<hr class="wp-block-separator"/>



<p class="wp-block-paragraph">Q：为什么合约代码不允许多线程？</p>



<p class="wp-block-paragraph">A：多个合约并发/并行执行可能会导致最终的状态无法一致。一个例子：</p>



<ol class="wp-block-list"><li>合约&nbsp;A：如果张三账户中至少有&nbsp;100&nbsp;个币则转给李四&nbsp;10&nbsp;个币。</li><li>合约&nbsp;B：如果张三账户中不足&nbsp;100&nbsp;个币则转给李四&nbsp;5&nbsp;个币。</li><li>合约&nbsp;A&nbsp;中现在恰好有&nbsp;100&nbsp;个币。</li><li>正常的执行顺序是先执行&nbsp;A，后执行&nbsp;B。运行结果是李四收到&nbsp;15&nbsp;个币，张三的账户剩余&nbsp;85&nbsp;个币。</li><li>但是如果是并发/并行执行，则可能先执行&nbsp;B，等待&nbsp;B&nbsp;完成后才执行&nbsp;A，这种情况下李四收到&nbsp;10&nbsp;个币，张三的账户剩余&nbsp;90&nbsp;个币。</li><li>也就是最终相关的状态会不一致，也就是状态的转移不确定。各个全节点之间无法达成共识。</li></ol>



<h2 class="wp-block-heading">参考资料</h2>



<ul class="wp-block-list"><li><a href="https://www.bilibili.com/video/BV1Vt411X7JF?p=22" data-type="URL" data-id="https://www.bilibili.com/video/BV1Vt411X7JF?p=22" target="_blank" rel="noreferrer noopener">北京大学肖臻老师《区块链技术与应用》公开课</a></li></ul>
]]></content:encoded>
					
					<wfw:commentRss>https://www.addesp.com/archives/179/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>以太坊 The DAO 事件</title>
		<link>https://www.addesp.com/archives/186</link>
					<comments>https://www.addesp.com/archives/186#respond</comments>
		
		<dc:creator><![CDATA[ADD-SP]]></dc:creator>
		<pubDate>Fri, 10 Jul 2020 14:30:03 +0000</pubDate>
				<category><![CDATA[以太坊]]></category>
		<guid isPermaLink="false">http://69a274df.addesp.com/?p=186</guid>

					<description><![CDATA[由于智能合约代码存储在区块链上，所以一旦其有漏洞也极难修复。TheDAO 是运行在 ETH 区块链平台上的一个智能合约，但是其代码存在漏洞，于是黑客发起了 Re-entrancy Attack 盗走了大量的以太币，最终导致了以太坊分裂为 ETH（Ethereum） 和 ETC（Ethereum Classic）。]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">概述</h2>



<p class="wp-block-paragraph">由于智能合约代码存储在区块链上，所以一旦其有漏洞也极难修复。TheDAO&nbsp;是运行在&nbsp;ETH&nbsp;区块链平台上的一个智能合约，但是其代码存在漏洞，于是黑客发起了&nbsp;Re-entrancy&nbsp;Attack&nbsp;盗走了大量的以太币，最终导致了以太坊分裂为&nbsp;ETH（Ethereum）&nbsp;和&nbsp;ETC（Ethereum&nbsp;Classic）。</p>



<p class="wp-block-paragraph"><br>本系列的文章不会着重于细节讲解，对于本次攻击的详情可以参考这篇文章：<a rel="noreferrer noopener" href="https://hackingdistributed.com/2016/06/18/analysis-of-the-dao-exploit/" data-type="URL" data-id="https://hackingdistributed.com/2016/06/18/analysis-of-the-dao-exploit/" target="_blank">Analysis&nbsp;of&nbsp;the&nbsp;DAO&nbsp;exploit</a>。</p>



<p class="wp-block-paragraph">将介绍下列内容：</p>



<ul class="wp-block-list"><li>TheDAO</li><li>补救措施</li><li>以太坊的分裂</li></ul>



<h2 class="wp-block-heading">TheDAO</h2>



<p class="wp-block-paragraph">DAO&nbsp;是&nbsp;Decentralized&nbsp;Autonomous&nbsp;Organization（分布式自治组织）的简称，TheDAO&nbsp;是一个基于&nbsp;ETH&nbsp;区块链平台的众筹项目。其目的是让持有&nbsp;TheDAO&nbsp;代币的参与者通过投票的方式共同决定被投资项目，整个社区完全自制，其功能通过智能合约实现。众筹完成时共募集&nbsp;1150&nbsp;万以太币，在当时的价值达到&nbsp;1.49&nbsp;亿美元。</p>



<h3 class="wp-block-heading">Child DAO</h3>



<p class="wp-block-paragraph">允许&nbsp;TheDAO&nbsp;代币的持有者调用&nbsp;TheDAO&nbsp;合约中的&nbsp;`split`&nbsp;函数，创建一个子合约，持有者在原有&nbsp;TheDAO&nbsp;智能合约中的&nbsp;TheDAO&nbsp;代币被销毁，存储在原&nbsp;TheDAO&nbsp;智能合约中对应的以太币被转移到新的&nbsp;TheDAO&nbsp;智能合约中。<br>Child&nbsp;DAO&nbsp;是为了保护处于弱势地位了&nbsp;TheDAO&nbsp;代币持有者，使他们也可以有一个可以投票和分红的&nbsp;DAO。黑客发起攻击后 ETC 就锁定在了 <strong>Child&nbsp;DAO</strong> 内。</p>



<h2 class="wp-block-heading">补救措施</h2>



<p class="wp-block-paragraph">Child&nbsp;DAO&nbsp;创建后有&nbsp;28&nbsp;天的锁定期，这段时间无法将以太币转走，就是段时间给了以太坊社区补救的机会。</p>



<p class="wp-block-paragraph">以太坊团队设计了两步走方案：</p>



<ol class="wp-block-list"><li>锁定黑客账户。</li><li>设法将盗取的以太币退还。</li></ol>



<p class="wp-block-paragraph">以太坊首先发布了一个软分叉方案，新的软件升级中加入了一条规则：凡是和&nbsp;TheDAO&nbsp;相关的账户一律不允许交易。升级发布后大多数矿工都升级的软件。但是新的软件有一个&nbsp;bug。<br>当新的软件执行某个交易与&nbsp;TheDAO&nbsp;相关时，这个交易失败，但是没有收取汽油费。这就导致了整个系统中充斥着大量的与&nbsp;TheDAO&nbsp;相关的交易，消耗了大量的资源，因此大多数矿工又将软件版本退回了更新前。<br>软分叉的措施失败了，此时距离&nbsp;28&nbsp;天的锁定期结束已经很近了。于是以太坊团队发布了一个硬分叉方案，所有&nbsp;TheDAO&nbsp;账户中的以太币都被强制转到另一个账户，事后再想办法将其退还。<br>这次没有出现意外，最终被盗取的以太币都退还了</p>



<h2 class="wp-block-heading">以太坊的分裂</h2>



<p class="wp-block-paragraph">在攻击发生后，以太坊社区有两个观点，双方进行了激烈讨论，最终没能达成一致，导致了分裂。</p>



<h3 class="wp-block-heading">应该采取措施</h3>



<p class="wp-block-paragraph">这一派认为应该采取措施方式黑客将以太币盗走，原因是&nbsp;TheDAO&nbsp;是太大而不能倒的合约，TheDAO&nbsp;筹集的以太币大概占有当时总流通量的&nbsp;10%&nbsp;左右。以太坊开发团队也持有这种观点。<br><em>但是不要认为合约出了问题以太坊团队就会介入，主要还是因为这个合约的影响太大了。</em></p>



<h3 class="wp-block-heading">不应采取措施</h3>



<p class="wp-block-paragraph">这一派认为黑客的行为并没有违反任何规则，因为智能合约的代码就是规则，按照规则行事何谈违反？<br>更重要的观点是，如果出了问题就要回滚，那么还谈什么去中心化的不可篡改的账本？开发团队一句话就能强行更改账本，这和中心化有何区别？</p>



<h3 class="wp-block-heading">分裂</h3>



<p class="wp-block-paragraph">两派的矿工都没能说服对方，最终导致以太坊分裂成了两条链，一条就是采取了措施的链，称为&nbsp;ETH（Ethereum），另一条是没有采取任何补救措施的链，称为&nbsp;ETC（Ethereum&nbsp;Classic）。直到现在这两条链依旧存在，只是&nbsp;ETH&nbsp;上的算力更强。<br>在&nbsp;ETC&nbsp;上挖矿人一部分是出于投机目的，因为这条链算力更少，更容易挖出区块。另一部分人是纯粹出于信仰，这些坚持十分纯正的去中心化理念，认为&nbsp;ETC&nbsp;才是正宗的以太坊，而&nbsp;ETH&nbsp;已经偏离的去中心化的初衷。</p>



<h3 class="wp-block-heading">参考资料</h3>



<ul class="wp-block-list"><li><a href="https://www.bilibili.com/video/BV1Vt411X7JF?p=23" data-type="URL" data-id="https://www.bilibili.com/video/BV1Vt411X7JF?p=23" target="_blank" rel="noreferrer noopener">北京大学肖臻老师《区块链技术与应用》公开课</a></li></ul>
]]></content:encoded>
					
					<wfw:commentRss>https://www.addesp.com/archives/186/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>以太坊的状态树、交易树和收据树</title>
		<link>https://www.addesp.com/archives/183</link>
					<comments>https://www.addesp.com/archives/183#respond</comments>
		
		<dc:creator><![CDATA[ADD-SP]]></dc:creator>
		<pubDate>Tue, 30 Jun 2020 14:45:02 +0000</pubDate>
				<category><![CDATA[以太坊]]></category>
		<guid isPermaLink="false">http://69a274df.addesp.com/?p=183</guid>

					<description><![CDATA[ETH 采用的是基于账户的模式，也就是说系统中显示记录着每个账户的当前状态信息。
每个账户都是由一个 160bit 的地址组成，外部账户中的状态包含余额（balance）、交易次数（nonce），合约账户包含 code(代码)、存储(stroge)。
对于一个出块时间只有十几秒的系统，如何高效地维护这些状态就成了十分重要的事情。]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">概述</h2>



<ul class="wp-block-list"><li>状态树</li><li>交易树和收据树</li></ul>



<h2 class="wp-block-heading">状态树</h2>



<p class="wp-block-paragraph">ETH&nbsp;采用的是基于账户的模式，也就是说系统中显示记录着每个账户的当前状态信息。</p>



<p class="wp-block-paragraph"><br>每个账户都是由一个&nbsp;160bit&nbsp;的地址组成，外部账户中的状态包含余额（balance）、交易次数（nonce），合约账户包含&nbsp;code(代码)、存储(stroge)。<br>对于一个出块时间只有十几秒的系统，如何高效地维护这些状态就成了十分重要的事情。</p>



<h3 class="wp-block-heading">为何不使用&nbsp;HashTable？</h3>



<p class="wp-block-paragraph">由于每个账户都是一个&nbsp;160bit&nbsp;的地址，所以很容易想到用&nbsp;HashTable&nbsp;来建立地址到账户状态的映射，在不考虑哈希碰撞的情况下，增删改查都是常数时间。国安民乐，岂不美哉？</p>



<p class="wp-block-paragraph"><br>那么为什么&nbsp;ETH&nbsp;没有使用&nbsp;HashTable&nbsp;呢？原因就是建立共识代价太大。</p>



<p class="wp-block-paragraph"><br>BTC&nbsp;建立共识是通过将区块内的交易组织成一颗&nbsp;Merkle&nbsp;Tree，并将根哈希值写入&nbsp;Block&nbsp;Header，其它全节点验证时只需要重新计算区根哈希值即可，同时因为一个&nbsp;Block&nbsp;的大小有限制，所以达成共识的代价比较小。<br>ETH&nbsp;如果使用&nbsp;HashTable&nbsp;管理账户状态的话，两个全节点要达成共识代价都很大，因为要遍历整个&nbsp;HashTable&nbsp;才可以。</p>



<h3 class="wp-block-heading">为何不使用&nbsp;Merkle&nbsp;Tree？</h3>



<p class="wp-block-paragraph">那么如果把账户的状态组织成一颗&nbsp;Merkle&nbsp;Tree&nbsp;怎么样呢？这样是不是就可以像&nbsp;BTC&nbsp;一样方便了？<br>看起来我们可以直接将根哈希值写入到&nbsp;Block&nbsp;Header&nbsp;里，以求向&nbsp;BTC&nbsp;一样达成共识。</p>



<hr class="wp-block-separator"/>



<p class="wp-block-paragraph"><br>Q：如果两个全节点记录的账户状态完全一致，它们的根哈希值一定相同么？</p>



<p class="wp-block-paragraph">A：不一定，因为两个全节点听到各个账户的顺序可能不同，如果不对&nbsp;Merkle&nbsp;Tree&nbsp;进行排序的话，两个全节点构造出来的&nbsp;Merkle&nbsp;Tree&nbsp;的根哈希值很可能不同。如果不同，也就无法达成共识。</p>



<hr class="wp-block-separator"/>



<p class="wp-block-paragraph"><br>Q：如果使用排序的&nbsp;Merkle&nbsp;Tree&nbsp;是不是就没问题了？</p>



<p class="wp-block-paragraph">A：如果监听到了一个新的账户，由于&nbsp;Merkle&nbsp;Tree&nbsp;时排序的，所以就要将其插入到合适的位置，那么就需要重新组织&nbsp;Merkle&nbsp;Tree，代价过大，不能接受。</p>



<h3 class="wp-block-heading">MPT（Merkle&nbsp;Patricia&nbsp;Trie）</h3>



<p class="wp-block-paragraph">最终&nbsp;ETH&nbsp;使用了&nbsp;MPT&nbsp;管理各个账户的状态，MPT&nbsp;是&nbsp;Merkle&nbsp;Tree&nbsp;和&nbsp;Patricia&nbsp;Trie&nbsp;组合而成的数据结构。</p>



<p class="wp-block-paragraph"><em>Patricia&nbsp;Trie&nbsp;就是经过路径压缩的&nbsp;Trie。</em></p>



<figure class="wp-block-image size-large"><img decoding="async" width="780" height="570" src="https://www.addesp.com/wp-content/uploads/2020/09/0-6.webp" alt="" class="wp-image-184"/></figure>



<p class="wp-block-paragraph">Q：新增一个账户时的代价是否可以接受？</p>



<p class="wp-block-paragraph">A：可以，因为&nbsp;160bit&nbsp;的账户地址很容易地就能添加到&nbsp;Patricia&nbsp;Trie，且由于&nbsp;Patricia&nbsp;Trie&nbsp;的特点，对其更新都是局部的，不会导致整棵树的重组。</p>



<hr class="wp-block-separator"/>



<p class="wp-block-paragraph"><br>Q：更改一个账户状态时的代价是否可以接受？</p>



<p class="wp-block-paragraph">A：可以，因为&nbsp;Patricia&nbsp;Trie&nbsp;的查找仅需对数时间，同时由于&nbsp;Patricia&nbsp;Trie&nbsp;的特点，其更改也是局部的，同样不会导致整棵树的重组。</p>



<hr class="wp-block-separator"/>



<p class="wp-block-paragraph"><br>Q：达成共识的代价是否可以接受？</p>



<p class="wp-block-paragraph">A：Merkle&nbsp;Patricia&nbsp;Trie&nbsp;中&nbsp;将&nbsp;Patricia&nbsp;Trie&nbsp;中的传统的地址指针改成了哈希指针，且由于只要存储的地址完全一致，则无论存储顺序的先后，Patricia&nbsp;Trie&nbsp;只有一种形态，所以可以算出&nbsp;Merkle&nbsp;Patricia&nbsp;Trie&nbsp;的根哈希值并写道&nbsp;Block&nbsp;Header&nbsp;中，达成共识十分简单。</p>



<hr class="wp-block-separator"/>



<p class="wp-block-paragraph">每个区块都会保存完整的状态树，而不是仅和当前区块内交易有关的节点。因为这样可以便于回滚。也可以这么考虑：</p>



<ol class="wp-block-list"><li>账户&nbsp;A&nbsp;转账给&nbsp;账户&nbsp;B&nbsp;一个币。</li><li>那么就应该先知道两个账户的余额。</li><li>如果每个区块只保存与当前区块有关联的账户的状态树节点。</li><li>且假设&nbsp;B&nbsp;是一个新的账户。</li><li>那么这个新账户肯定不会存在于状态树中。</li><li>由于每个区块没有保留完整的状态树，所以只能顺着区块链去找。</li><li>那么查询账户&nbsp;B&nbsp;的状态，最终会找到&nbsp;Genesis&nbsp;Block，才会发现这是一个新账户。</li><li>代价太大，不能接受。</li></ol>



<h2 class="wp-block-heading">交易树和收据树</h2>



<p class="wp-block-paragraph">ETH&nbsp;每次发布区块的时候其包含的交易会被组织成一颗交易树，每笔交易执行完后会产生一个收据，记录相关信息，最终也会被组织成收据树，这两棵树均为&nbsp;MPT。<br>交易树和收据树也可以提供根哈希值。同时&nbsp;ETH&nbsp;也提供了比较复杂的查询操作，如查询&nbsp;10&nbsp;天内和某个智能合约有交易的账户等。为了降低这类查询操作的代价，ETH&nbsp;使用了&nbsp;Bloom&nbsp;Filter&nbsp;，它可以让矿工在沿着区块链查找交易等信息时快速地排除不可能包含所需信息区块。</p>



<p class="wp-block-paragraph"><em><a rel="noreferrer noopener" href="https://baike.baidu.com/item/%E5%B8%83%E9%9A%86%E8%BF%87%E6%BB%A4%E5%99%A8/5384697" data-type="URL" data-id="https://baike.baidu.com/item/%E5%B8%83%E9%9A%86%E8%BF%87%E6%BB%A4%E5%99%A8/5384697" target="_blank">Bloom&nbsp;Filter</a></em></p>



<h2 class="wp-block-heading">参考资料</h2>



<ul class="wp-block-list"><li><a href="https://www.bilibili.com/video/BV1Vt411X7JF?p=16" data-type="URL" data-id="https://www.bilibili.com/video/BV1Vt411X7JF?p=16" target="_blank" rel="noreferrer noopener">北京大学肖臻老师《区块链技术与应用》公开课</a></li></ul>
]]></content:encoded>
					
					<wfw:commentRss>https://www.addesp.com/archives/183/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>以太坊的 GHOST 协议</title>
		<link>https://www.addesp.com/archives/171</link>
					<comments>https://www.addesp.com/archives/171#respond</comments>
		
		<dc:creator><![CDATA[ADD-SP]]></dc:creator>
		<pubDate>Tue, 30 Jun 2020 14:18:55 +0000</pubDate>
				<category><![CDATA[以太坊]]></category>
		<guid isPermaLink="false">http://69a274df.addesp.com/?p=171</guid>

					<description><![CDATA[ETH 的出块时间大概只有十几秒，如此短的出块时间如果照搬 BTC 的共识机制就会造成大量的分叉，分散全网算力，降低系统的安全性。因此 ETH 采用了新的 GHOST 协议，可以很好地适应十几秒地出块时间，保证系统的安全性，同时也在一定程度上平衡了收益，使得大型矿池相对于 BTC 不再那么占便宜。：]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">概述</h2>



<p class="wp-block-paragraph">ETH&nbsp;的出块时间大概只有十几秒，如此短的出块时间如果照搬&nbsp;BTC&nbsp;的共识机制就会造成大量的分叉，分散全网算力，降低系统的安全性。因此&nbsp;ETH&nbsp;采用了新的&nbsp;GHOST&nbsp;协议，可以很好地适应十几秒地出块时间，保证系统的安全性，同时也在一定程度上平衡了收益，使得大型矿池相对于&nbsp;BTC&nbsp;不再那么占便宜。<br>使用基于&nbsp;GHOST&nbsp;的协议主要出于两个目的：</p>



<ul class="wp-block-list"><li>维护系统安全性</li><li>平衡收益</li></ul>



<h2 class="wp-block-heading">维护系统安全性</h2>



<p class="wp-block-paragraph">出块时间只有十几秒，由于&nbsp;Flooding&nbsp;的时候没有考虑底层拓扑，如此短的时间一个区块难以广播给全网，容易造成大量分叉，如果继续使用&nbsp;BTC&nbsp;的共识协议，极其容易分散算力，降低系统安全性。</p>



<h2 class="wp-block-heading">平衡收益</h2>



<p class="wp-block-paragraph">以下图所示情况为例说明</p>



<figure class="wp-block-image size-large"><img decoding="async" width="482" height="262" src="https://www.addesp.com/wp-content/uploads/2020/09/0-5.webp" alt="" class="wp-image-172"/></figure>



<p class="wp-block-paragraph">我们假设&nbsp;A、C&nbsp;两个&nbsp;Block&nbsp;时个人矿工所挖出的，B&nbsp;是大型矿池挖出的&nbsp;Block。假设三个&nbsp;Block&nbsp;同时挖出。</p>



<ol class="wp-block-list"><li>显然大型矿池的所在的链最有可能成为最合法链，也就是&nbsp;B&nbsp;所在的链。</li><li>同时其它矿工出于不想浪费算力的目的，通常会选择跟在大型矿池的后面继续挖矿，而不是跟在个人区块后面。</li><li>理想情况下如果一个矿工占有&nbsp;n%&nbsp;的算力，那么他的收益也应该是所有矿工收益的&nbsp;n%。</li><li>现在&nbsp;B&nbsp;所在的链成为最长合法链的概率会超过&nbsp;n%。</li><li>因为个体矿工愿意跟随大型矿池进行挖矿。</li><li>那么大型矿池的期望收益就会超过&nbsp;n%。</li><li>我们不希望这种情况出现。</li></ol>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="582" height="262" src="https://www.addesp.com/wp-content/uploads/2020/09/1-5.webp" alt="" class="wp-image-173"/></figure>



<p class="wp-block-paragraph">上图这种情况下，ETH&nbsp;规定，D&nbsp;在发布的时候可以将&nbsp;A、C&nbsp;作为&nbsp;Uncle&nbsp;Block&nbsp;包含进&nbsp;D&nbsp;中，那么&nbsp;A&nbsp;和&nbsp;C&nbsp;的其所有者会获得&nbsp;7/8&nbsp;的出块奖励。一个区块中最多只能包含两个&nbsp;Uncle&nbsp;Block。</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="782" height="262" src="https://www.addesp.com/wp-content/uploads/2020/09/2-5.webp" alt="" class="wp-image-174"/></figure>



<p class="wp-block-paragraph">对于上图这种情况，能被包含进&nbsp;J&nbsp;的&nbsp;Uncle&nbsp;Block&nbsp;只能是&nbsp;A&nbsp;和&nbsp;C。而&nbsp;E、G、F、H&nbsp;不能作为&nbsp;Uncle&nbsp;Block，否则这会降低矿工合并分支的积极性。</p>



<p class="wp-block-paragraph"><br>Uncle&nbsp;Block&nbsp;最多往前追溯七代，收益每隔一代减少&nbsp;1/8，七代之前的区块不允许作为&nbsp;Uncle&nbsp;Block。<br></p>



<p class="wp-block-paragraph">这么做就可以在一定程度上平衡了收益，也能鼓励分叉尽快进行合并。</p>



<h2 class="wp-block-heading">参考资料</h2>



<ul class="wp-block-list"><li><a href="https://www.bilibili.com/video/BV1Vt411X7JF?p=18" data-type="URL" data-id="https://www.bilibili.com/video/BV1Vt411X7JF?p=18" target="_blank" rel="noreferrer noopener">北京大学肖臻老师《区块链技术与应用》公开课</a></li></ul>
]]></content:encoded>
					
					<wfw:commentRss>https://www.addesp.com/archives/171/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>以太坊的账户</title>
		<link>https://www.addesp.com/archives/169</link>
					<comments>https://www.addesp.com/archives/169#respond</comments>
		
		<dc:creator><![CDATA[ADD-SP]]></dc:creator>
		<pubDate>Sat, 27 Jun 2020 14:37:24 +0000</pubDate>
				<category><![CDATA[以太坊]]></category>
		<guid isPermaLink="false">http://69a274df.addesp.com/?p=169</guid>

					<description><![CDATA[外部账户 &#038; 合约账户]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">概述</h2>



<ul class="wp-block-list"><li>外部账户（Externally&nbsp;Owned&nbsp;Accounts）</li><li>合约账户（Smart&nbsp;Contract&nbsp;Accounts）</li></ul>



<p class="wp-block-paragraph">BTC&nbsp;采用基于交易的模型，一个账户有多少币没有显式记录，如果想要知道只能顺着区块链去查&nbsp;UTXO&nbsp;才能知道。</p>



<p class="wp-block-paragraph"><br>ETH&nbsp;采用基于账户的模型，就像现实中的银行账户一样，一个账户有多少币是显式记录在区块链上的，每次支付的时候就检测余额并修改余额即可，无需关注币的来源。</p>



<p class="wp-block-paragraph"><br>为什么&nbsp;ETH&nbsp;会使用这种模式呢？因为&nbsp;ETH&nbsp;要支持智能合约（Smart&nbsp;Contract），这就要求参与者的身份要比较稳定，否则很难知道合约的参与者究竟是哪个账户</p>



<h2 class="wp-block-heading">外部账户（Externally&nbsp;Owned&nbsp;Accounts）</h2>



<p class="wp-block-paragraph">外部账账户内记录的下列内容：</p>



<ul class="wp-block-list"><li>balance：余额</li><li>nonce：不同于&nbsp;BTC，这里则表示该账户发起过多少笔交易。</li></ul>



<p class="wp-block-paragraph">账户的信息都会记录到一种名为「状态树」的数据结构中，每次交易都会更新状态树的对应节点。</p>



<h3 class="wp-block-heading">重放攻击（Replay&nbsp;Attack）</h3>



<p class="wp-block-paragraph">基于账户的模式天然地对双花攻击（Double&nbsp;Spending&nbsp;Attack）有防范作用，但是容易成为重放攻击的目标。</p>



<p class="wp-block-paragraph"><br>重放攻击就是将一个已经上链的交易重新放送一次，由于这笔交易具有被攻击账户的签名，所以全节点会认为其是有效的，所以会导致一笔交易重复执行。对此&nbsp;ETH&nbsp;通过`nonce`字段来防范，这个字段记录了该账户发起过多少笔交易，每笔交易都要记录当前的`nonce`值，这样就可以有效防范重放攻击了。因为当一笔交易被重放时，其`nonce`值会比账户中记录的`nonce`值小，由此可以判断该账户没有发起过这笔交易。</p>



<h2 class="wp-block-heading">合约账户（Smart&nbsp;Contract&nbsp;Accounts）</h2>



<p class="wp-block-paragraph">合约账户记录下列内容：</p>



<ul class="wp-block-list"><li>Code：即合约代码</li><li>Storage：相关数据</li></ul>



<p class="wp-block-paragraph">合约内的代码可以被其它用户所调用，也可以被其它合约调用，用以完成合约的功能。创建合约的时候会返回一个地址，根据这个地址即可调用合约代码。<br>智能合约代码一般使用&nbsp;Solidity&nbsp;语言编写，运行在&nbsp;EVM（Ethereum&nbsp;Virtual&nbsp;Machine）上，且代码一旦上链，则极难更改。</p>



<h2 class="wp-block-heading">参考资料</h2>



<ul class="wp-block-list"><li><a href="https://www.bilibili.com/video/BV1Vt411X7JF?p=15" data-type="URL" data-id="https://www.bilibili.com/video/BV1Vt411X7JF?p=15" target="_blank" rel="noreferrer noopener">北京大学肖臻老师《区块链技术与应用》公开课</a></li></ul>
]]></content:encoded>
					
					<wfw:commentRss>https://www.addesp.com/archives/169/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>以太坊概述</title>
		<link>https://www.addesp.com/archives/177</link>
					<comments>https://www.addesp.com/archives/177#respond</comments>
		
		<dc:creator><![CDATA[ADD-SP]]></dc:creator>
		<pubDate>Sat, 27 Jun 2020 14:07:49 +0000</pubDate>
				<category><![CDATA[以太坊]]></category>
		<guid isPermaLink="false">http://69a274df.addesp.com/?p=177</guid>

					<description><![CDATA[ASIC Resistance &#038; GHOST 协议 &#038; 基于账户的交易模式（Account-based ledger）&#038; 权益证明（Proof Of Stake） &#038; 智能合约（Smart Contract）]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">概述</h2>



<ul class="wp-block-list"><li>ASIC Resistance</li><li>GHOST 协议</li><li>基于账户的交易模式（Account-based ledger）</li><li>权益证明（Proof Of Stake）</li><li>智能合约（Smart Contract）</li></ul>



<h2 class="wp-block-heading">ASIC&nbsp;Resistance</h2>



<p class="wp-block-paragraph">BTC&nbsp;起初的设计目的包括让通用计算机加入到挖矿过程，但是目前&nbsp;BTC&nbsp;的挖矿主要是由&nbsp;ASIC&nbsp;矿机完成的，使用通用计算机挖矿几乎是无利可图的。</p>



<p class="wp-block-paragraph"><br>而&nbsp;ETH&nbsp;希望避免这种情况，其设计了&nbsp;Member&nbsp;hard&nbsp;mining&nbsp;puzzle，因为&nbsp;ASIC&nbsp;矿机的访存效率不一定有通用计算机高，如果&nbsp;Mining&nbsp;puzzle&nbsp;对内存的要求较高，则可以很好地对抗&nbsp;ASIC&nbsp;矿机。</p>



<h2 class="wp-block-heading">GHOST&nbsp;协议</h2>



<p class="wp-block-paragraph">ETH&nbsp;的出块时间大概只有十几秒，如此短的出块时间如果照搬&nbsp;BTC&nbsp;的共识机制就会造成大量的分叉，分散全网算力，降低系统的安全性。因此&nbsp;ETH&nbsp;采用了新的&nbsp;GHOST&nbsp;协议，可以很好地适应十几秒地出块时间，保证系统的安全性。</p>



<h2 class="wp-block-heading">基于账户的交易模式（Account-based&nbsp;ledger）</h2>



<p class="wp-block-paragraph">BTC&nbsp;中一个矿工有多少币是没有显式记录的，想要知道余额只能去查&nbsp;UTXO，而&nbsp;ETH&nbsp;是基于账户的交易模式，自己有多少币很容易就知道了。所有账户的信息存储在全节点的状态树，当某个账户发起交易时，全节点会检查账户信息以确定交易的合法性。当然这种模式的优点不止一个，当然有了新的缺点，笔者将会在其它文章中说明。</p>



<h2 class="wp-block-heading">权益证明（Proof&nbsp;Of&nbsp;Stake）</h2>



<p class="wp-block-paragraph">BTC&nbsp;采用的是&nbsp;Proof&nbsp;Of&nbsp;Work，即「工作量证明」，而&nbsp;ETH&nbsp;则计划逐步转为&nbsp;Proof&nbsp;Of&nbsp;Stake，即「权益证明」，这也是&nbsp;ETH&nbsp;对抗&nbsp;ASIC&nbsp;矿机的一种方式。权益证明简单来说就是持有的币越多，在系统中的话语权越大。详细情况笔者将会在其它文章中说明。</p>



<h2 class="wp-block-heading">智能合约（Smart&nbsp;Contract）</h2>



<p class="wp-block-paragraph">智能合约是存储在区块链上的一段代码，可以被调用。<br>BTC&nbsp;的脚本语言只能实现一些简单的操作。而&nbsp;ETH&nbsp;常常用于书写智能合约的语言&nbsp;Solidity&nbsp;是图灵完备的，这代表其可以又有十分强大的功能。<br>智能合约作为去中心化的合约，其合约的有效性是靠&nbsp;ETH&nbsp;系统的安全性保证的。</p>



<h2 class="wp-block-heading">参考资料</h2>



<ul class="wp-block-list"><li><a href="https://www.bilibili.com/video/BV1Vt411X7JF?p=14" data-type="URL" data-id="https://www.bilibili.com/video/BV1Vt411X7JF?p=14" target="_blank" rel="noreferrer noopener">北京大学肖臻老师《区块链技术与应用》公开课</a></li></ul>
]]></content:encoded>
					
					<wfw:commentRss>https://www.addesp.com/archives/177/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>针对比特币的「51%算力攻击」</title>
		<link>https://www.addesp.com/archives/94</link>
					<comments>https://www.addesp.com/archives/94#respond</comments>
		
		<dc:creator><![CDATA[ADD-SP]]></dc:creator>
		<pubDate>Sun, 17 May 2020 13:25:02 +0000</pubDate>
				<category><![CDATA[比特币]]></category>
		<guid isPermaLink="false">http://69a274df.addesp.com/?p=94</guid>

					<description><![CDATA[Double spending attack &#038; Boycott]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">概述</h2>



<ul class="wp-block-list"><li>Double&nbsp;spending&nbsp;attack（双花攻击）</li><li>Boycott</li></ul>



<h2 class="wp-block-heading">Double&nbsp;spending&nbsp;attack</h2>



<p class="wp-block-paragraph">即双花攻击，一个比特币可以花至少两次。</p>



<h3 class="wp-block-heading">攻击举例</h3>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="501" height="211" src="https://www.addesp.com/wp-content/uploads/2020/09/0.webp" alt="" class="wp-image-95"/></figure>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="961" height="211" src="https://www.addesp.com/wp-content/uploads/2020/09/1.webp" alt="" class="wp-image-96"/></figure>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="961" height="321" src="https://www.addesp.com/wp-content/uploads/2020/09/2.webp" alt="" class="wp-image-97"/></figure>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1041" height="342" src="https://www.addesp.com/wp-content/uploads/2020/09/3.webp" alt="" class="wp-image-98"/></figure>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1041" height="271" src="https://www.addesp.com/wp-content/uploads/2020/09/4.webp" alt="" class="wp-image-99"/></figure>



<h2 class="wp-block-heading">Boycott</h2>



<p class="wp-block-paragraph">即可以通过拒绝某些或某个账户的交易来达到封锁这个账户的目的。</p>



<h3 class="wp-block-heading">攻击举例</h3>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="501" height="303" src="https://www.addesp.com/wp-content/uploads/2020/09/5.webp" alt="" class="wp-image-101"/></figure>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1231" height="386" src="https://www.addesp.com/wp-content/uploads/2020/09/6.webp" alt="" class="wp-image-102"/></figure>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1231" height="386" src="https://www.addesp.com/wp-content/uploads/2020/09/7.webp" alt="" class="wp-image-103"/></figure>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="801" height="216" src="https://www.addesp.com/wp-content/uploads/2020/09/8.webp" alt="" class="wp-image-104"/></figure>



<h2 class="wp-block-heading">参考资料</h2>



<ul class="wp-block-list"><li><a href="https://www.bilibili.com/video/BV1Vt411X7JF" data-type="URL" data-id="https://www.bilibili.com/video/BV1Vt411X7JF" target="_blank" rel="noreferrer noopener">北京大学肖臻老师《区块链技术与应用》公开课</a></li></ul>
]]></content:encoded>
					
					<wfw:commentRss>https://www.addesp.com/archives/94/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>比特币的匿名性</title>
		<link>https://www.addesp.com/archives/19</link>
					<comments>https://www.addesp.com/archives/19#respond</comments>
		
		<dc:creator><![CDATA[ADD-SP]]></dc:creator>
		<pubDate>Sun, 17 May 2020 08:19:00 +0000</pubDate>
				<category><![CDATA[比特币]]></category>
		<guid isPermaLink="false">http://69a274df.addesp.com/?p=19</guid>

					<description><![CDATA[因比特币的底层技术为区块链，所以其天然的具有匿名性。]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">概述</h2>



<ul class="wp-block-list"><li>何为匿名</li><li><s>如何提高比特币用户的匿名性</s></li><li>什么情况下会破坏比特币的匿名性</li><li>典型案例</li><li>零知识证明</li><li>零币和零钞</li></ul>



<h2 class="wp-block-heading">何为匿名</h2>



<p class="wp-block-paragraph">比特币的匿名性个人我个人理解就是如果我自己不说，别人不会知道这个哪些账户是我的，以及这些我和某个具体人的发生了比特币交易。</p>



<h2 class="wp-block-heading">什么情况下会破坏比特币的匿名性</h2>



<ul class="wp-block-list"><li>当比特币和实体世界发生联系的时候，比如我用比特币换了现实中流通的货币，或者用比特币买了某件商品，甚至是用某台电脑登录了比特币账户。</li><li>区块链中交易信息是完全公开的，可以通过时间来找出账户对应的使用者。如我早上八点看见张三用比特币一杯豆浆，中午十二点看见张三用比特币点了一份外卖。这时我去查区块链中的对应时间点的交易记录，则就可以大大缩小寻找范围，长此以往，很容易就能知道张三使用的时哪个账户。</li></ul>



<h2 class="wp-block-heading">典型案例</h2>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"><p>Silk&nbsp;Road，（丝路）是一个利用Tor的隐密服务来运作的黑市购物网站，Tor的服务保证了网站用户的匿名性。你能想到的东西都有可能在里面找到。只要你情我愿，就可以形成一笔买卖。毒品、性奴、儿童色情、私人杀手，这里没有“非法交易”，用比特币结算还使得交易变得无法追踪，从而受到了大量毒贩的青睐。</p><cite>https://baike.baidu.com/item/Silk%20Road</cite></blockquote>



<p class="wp-block-paragraph">最终幕后老板在旧金山被捕了，更重要的一点是此人一个比特币都没有花，也就是说没有用比特币换取过实体世界的任何物品和货币。后来出现了<strong> Silk&nbsp;Road&nbsp;2</strong>，没几年也被查封了</p>



<h2 class="wp-block-heading">零知识证明</h2>



<p class="wp-block-paragraph">零知识证明是指一方（证明者）向另一方（验证者）证明一个陈述是正确的，而无需透露除该陈述是正确的外的任何信息。</p>



<h3 class="wp-block-heading">同态隐藏</h3>



<ul class="wp-block-list"><li>如果&nbsp;$x&nbsp;\neq&nbsp;y$，那么它们的加密函数值&nbsp;$E(x),E(y)$&nbsp;也不相同。</li><li>给定&nbsp;$E(x)$&nbsp;的值，很难反推出$x$的值。</li><li>给定$E(x)$&nbsp;和$E(y)$的值，可以很容易地计算出关于$x,y$的加密函数值。<ul><li>同态加法：通过$E(x)$和$E(y)$计算出$E(x + y)$。</li><li>同态乘法：通过$E(x)$和$E(y)$计算出$E(x \times y)$。</li><li>上述的加法乘法均可扩展到多项式。</li></ul></li></ul>



<h3 class="wp-block-heading">例子</h3>



<p class="wp-block-paragraph">Alice&nbsp;想要向&nbsp;Bob&nbsp;证明她知道一组数&nbsp;$x,y&nbsp;且&nbsp;x+y=7$，同时不想让&nbsp;Bob&nbsp;知道$x$&nbsp;和$y$&nbsp;的具体数值。</p>



<ol class="wp-block-list"><li>Alice&nbsp;向&nbsp;Bob&nbsp;提供$E(x)$&nbsp;和$E(y)$的值。</li><li>Bob&nbsp;通过$E(x)$&nbsp;和$E(y)$计算出$E(x+y)$的值，并计算$E(7)$的值。</li><li>如果&nbsp;$E(x+y)=E(7)$则代表&nbsp;Alice&nbsp;没有说谎。</li><li>此时&nbsp;Bob&nbsp;难以知道$x$和$y$的具体数值。</li></ol>



<h3 class="wp-block-heading">盲签</h3>



<p class="wp-block-paragraph">在签名者不知道被签名信息的具体内容下对其进行签名。具体信息可自行查询资料详细了解。</p>



<h2 class="wp-block-heading">零币和零钞</h2>



<p class="wp-block-paragraph">零币和零钞在协议层就融合了匿名化处理，其匿名性来自密码学保证。<br>但其并不安全，因为要用其牟利总归要与实体世界发生交互。</p>



<p class="has-vivid-red-color has-text-color wp-block-paragraph"><strong>不要用零币和零钞进行任何违法犯罪活动。天网恢恢，疏而不漏。</strong></p>



<h3 class="wp-block-heading">零币</h3>



<p class="wp-block-paragraph">零币系统中存在基础币和零币，通过基础币和零币之间的转换来消除旧地址和新地址之间的关联。</p>



<h3 class="wp-block-heading">零钞</h3>



<p class="wp-block-paragraph">零钞系统使用&nbsp;zk-SNARKs&nbsp;协议，不依赖基础币，只记录交易的存在性和矿工用来验证的系统正常运行的关键信息，区块链上既不现实交易地址也不显示交易金额，所有交易通过零知识验证的方式进行。</p>



<h2 class="wp-block-heading">参考资料</h2>



<ul class="wp-block-list"><li><a href="https://www.bilibili.com/video/BV1Vt411X7JF" data-type="URL" data-id="https://www.bilibili.com/video/BV1Vt411X7JF" target="_blank" rel="noreferrer noopener">北京大学肖臻老师《区块链技术与应用》公开课</a></li></ul>



<p class="wp-block-paragraph"></p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.addesp.com/archives/19/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>比特币的分叉</title>
		<link>https://www.addesp.com/archives/119</link>
					<comments>https://www.addesp.com/archives/119#respond</comments>
		
		<dc:creator><![CDATA[ADD-SP]]></dc:creator>
		<pubDate>Sat, 16 May 2020 13:57:11 +0000</pubDate>
				<category><![CDATA[比特币]]></category>
		<guid isPermaLink="false">http://69a274df.addesp.com/?p=119</guid>

					<description><![CDATA[分叉的原因 &#038; 软分叉 &#038; 硬分叉]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">概述</h2>



<ul class="wp-block-list"><li>产生分叉的原因</li><li>Hard&nbsp;fork</li><li>Soft&nbsp;fork</li></ul>



<h2 class="wp-block-heading">产生分叉的原因</h2>



<ul class="wp-block-list"><li>比特币系统的协议的修改升级，因此产生的分叉称为&nbsp;Protocd&nbsp;fork。</li><li>日常挖矿过程中也会产生一些分叉，但是通常会很快地淘汰掉其它分叉，只留下一个。</li><li>攻击者的攻击。</li></ul>



<h2 class="wp-block-heading">Hard&nbsp;fork</h2>



<p class="wp-block-paragraph">即这个分叉是永远的。<br>如果对比特币协议增加新的特性，那些没有升级软件的节点不认可新特性，这个时候就会产生硬分叉，一个例子是比特币的区块大小限制。假如有人发布了软件更新，将区块大小限制从&nbsp;1M→4M，假设大多数节点都更新了，少数节点没有更新，这里的大多数不是按账户数目来算，而是按算力来算，即系统中有大多数&nbsp;hash&nbsp;算力的节点都更新了软件。新节点能挖出最大是&nbsp;4M&nbsp;的区块，但是旧节点不认可大小超过1M的区块，所以旧节点会沿着小区块一直挖下去，新节点大小区块都接受，但旧节点不接受大区块，两者没有达成共识，这样就会出现一个永远的分叉。</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="901" height="341" src="https://www.addesp.com/wp-content/uploads/2020/09/0-2.webp" alt="" class="wp-image-120"/></figure>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="921" height="391" src="https://www.addesp.com/wp-content/uploads/2020/09/1-2.webp" alt="" class="wp-image-121"/></figure>



<h2 class="wp-block-heading">Soft&nbsp;fork</h2>



<p class="wp-block-paragraph">即暂时性的分叉。<br>如果对比特币协议加一些限制，原来合法的交易或者区块在新的协议可能不合法就会引起软分叉。假如有人发布更新将区块大小变成&nbsp;0.5M，大部分节点更新了软件，少部分没有更新。这时候新节点沿着一条链挖小区块，不认可大区块，旧节点沿着一条链挖大区块，同时旧节点也认可小区块，因为新节点算力更强，所以更快形成一条新旧节点都认可的最长合法链，但是新节点不会在旧节点产出的大区块后面继续挖，而是会继续分叉挖小区块，所以旧节点挖出的大区块最后都没用了，不得不更新软件。</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="911" height="363" src="https://www.addesp.com/wp-content/uploads/2020/09/2-2.webp" alt="" class="wp-image-122"/></figure>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1377" height="441" src="https://www.addesp.com/wp-content/uploads/2020/09/3-2.webp" alt="" class="wp-image-123"/></figure>



<h2 class="wp-block-heading">参考资料</h2>



<ul class="wp-block-list"><li><a href="https://www.bilibili.com/video/BV1Vt411X7JF" data-type="URL" data-id="https://www.bilibili.com/video/BV1Vt411X7JF" target="_blank" rel="noreferrer noopener">北京大学肖臻老师《区块链技术与应用》公开课</a></li></ul>
]]></content:encoded>
					
					<wfw:commentRss>https://www.addesp.com/archives/119/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>比特币的趋势</title>
		<link>https://www.addesp.com/archives/145</link>
					<comments>https://www.addesp.com/archives/145#respond</comments>
		
		<dc:creator><![CDATA[ADD-SP]]></dc:creator>
		<pubDate>Sat, 16 May 2020 13:36:41 +0000</pubDate>
				<category><![CDATA[比特币]]></category>
		<guid isPermaLink="false">http://69a274df.addesp.com/?p=145</guid>

					<description><![CDATA[CPU 挖矿 &#038; GPU 挖矿 &#038; ASIC 挖矿 &#038; 大型矿池]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">概述</h2>



<ul class="wp-block-list"><li>CPU 挖矿</li><li>GPU 挖矿</li><li>ASIC 挖矿</li><li>大型矿池</li></ul>



<h2 class="wp-block-heading">CPU 挖矿</h2>



<p class="wp-block-paragraph">起初挖矿竞争还不激烈，大家可以随意用自己的&nbsp;PC&nbsp;进行挖矿，出块的概率也算可以接受。但是随着竞争的激烈，CPU&nbsp;无法满足算力的需求，因为通用计算机的的CPU在挖矿过程中只用到了&nbsp;CPU&nbsp;指令的很少的一部分。</p>



<h2 class="wp-block-heading">GPU 挖矿</h2>



<p class="wp-block-paragraph">这时大家开始用GPU挖矿，因为&nbsp;GPU&nbsp;是专门为了通用的大规模的并行运算而开发的，效率要比&nbsp;CPU&nbsp;挖矿提高了很多。<br>但是由于&nbsp;GPU&nbsp;依然是为了通用计算而开发，所以依然有很多功能无法利用导致浪费，如浮点运算。</p>



<h2 class="wp-block-heading">ASIC 挖矿</h2>



<p class="wp-block-paragraph">Application&nbsp;Specific&nbsp;Integrated&nbsp;Circuit，专门为了某种需求开发的芯片。随着挖矿竞争的加强，也有了专门用于挖矿的芯片，这种芯片的性价比是很高的，效率也远高于&nbsp;GPU。<br>据统计，此类芯片的主要利润主要来自发布的前两个月，两个月后基本就会被淘汰。矿工之间的竞争就像是军备竞赛。</p>



<h2 class="wp-block-heading">大型矿池</h2>



<p class="wp-block-paragraph">矿工单干时就像是买彩票，出块的概率很低，但是一旦出块了就能大赚一笔。这时就有了矿池的出现。<br>矿场主号召矿工加入自己的矿池，由矿场主分配不同的任务给每个矿工，矿工出块后就提交给矿池，大家一起分享收益。区块由矿场主组装，矿工只负责计算 Hash，矿场主则会收取一部分管理费。<br>矿池的出现使得矿工的收益相对来说更加稳定，但同时单次收益也会减少。</p>



<h3 class="wp-block-heading">如何分配收益</h3>



<p class="wp-block-paragraph">首先肯定不能是平均分配，因为这种方式会导致干多干少收益都是一样的。矿场主可以自己规定一个挖矿难度，这个难度小于比特币网络所规定的挖矿难度。矿工挖出出块后，如果符合矿池规定的难度但不符合比特币网络的难度则这个区块可以作为工作量证明提交给矿池，这种区块称为&nbsp;Almost&nbsp;valid&nbsp;block，如果符合比特币网络的难度则可以作为整个矿池收益的一部分。最后按照每个矿工提交的两种区块的数量来分配收益。</p>



<p class="wp-block-paragraph">Q：矿工可否挖到合法区块后绕过矿主自己去提交从而独吞奖励，而&nbsp;Almost&nbsp;valid&nbsp;block&nbsp;作为工作量证明提交给矿池？</p>



<p class="wp-block-paragraph">A：不可以，因为区块中的收款人是矿场主，就算矿工自己发布也赚不到比特币，比特币依旧是矿场主的。</p>



<h3 class="wp-block-heading">担忧</h3>



<p class="wp-block-paragraph">矿池的出现使得算力的集中更加简单，因为原本大家都是各干各的，而现在矿工加入和更换矿池是非常容易的。此时如果矿场主以较低的管理费吸引大量的矿工以获得半数以上的算力，同时因为每个矿工只负责计算 Hash ，因此其不知道自己是否在发起攻击，所以可以以此发起「51%算力攻击」。<br>而如果没有矿池，攻击者需要自己购置硬件来对抗全网的算力，成本十分巨大。</p>



<h2 class="wp-block-heading">参考资料</h2>



<ul class="wp-block-list"><li><a href="https://www.bilibili.com/video/BV1Vt411X7JF" data-type="URL" data-id="https://www.bilibili.com/video/BV1Vt411X7JF" target="_blank" rel="noreferrer noopener">北京大学肖臻老师《区块链技术与应用》公开课</a></li></ul>
]]></content:encoded>
					
					<wfw:commentRss>https://www.addesp.com/archives/145/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>比特币的挖矿难度</title>
		<link>https://www.addesp.com/archives/109</link>
					<comments>https://www.addesp.com/archives/109#respond</comments>
		
		<dc:creator><![CDATA[ADD-SP]]></dc:creator>
		<pubDate>Thu, 14 May 2020 01:19:06 +0000</pubDate>
				<category><![CDATA[比特币]]></category>
		<guid isPermaLink="false">http://69a274df.addesp.com/?p=109</guid>

					<description><![CDATA[为何调整难度 &#038; 如何调整难度]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">概述</h2>



<ul class="wp-block-list"><li>为何调整挖矿难度</li><li>如何调整挖矿难度</li></ul>



<h2 class="wp-block-heading">为何调整挖矿难度</h2>



<p class="wp-block-paragraph">全网的算力可能会增强，可能会减弱，比特币系统会系统的出块时间控制在10分钟左右（中本聪没有说为什么是10分钟）。</p>



<p class="wp-block-paragraph"><em>中本聪并没有说明为何是10分钟，可能是他/她喜欢。</em></p>



<h3 class="wp-block-heading">如果挖矿难度过低</h3>



<p class="wp-block-paragraph">如果挖矿难度太低，则代表出块时间会非常短，看起来是好事，因为我发布一笔交易会很快地被写入到区块中。但是也会出现很大的问题。</p>



<ol class="wp-block-list"><li>因为出块时间缩短，所以会出现很多节点几乎在同一个时间发布区块。</li><li>在出块慢的时候10分钟的时间基本足够将出块信息广播给网络中的绝大多数节点，而在十分钟内基本不会出现大量的区块被广播。而如果出块速度加快，则在某个区块的广播过程中别的节点也很容易算出自己的区块并广播，这就会导致网络中的节点收到的区块不是基本一致的，而是多种多样的。</li><li>所以很容易造成区块链的分叉，而且可能不是两叉，数量可能会很多。</li><li>这样全网的算力就被分散到十分多的分叉上了。</li><li>此时如果攻击者发起一些攻击如&nbsp;Double&nbsp;Spending&nbsp;Attack，可能就不需要全网51%的算力了，因为全网的算力被分散了，而不是集中在一条最长链上。</li></ol>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="562" height="212" src="https://www.addesp.com/wp-content/uploads/2020/09/0-1.webp" alt="" class="wp-image-110"/></figure>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="651" height="279" src="https://www.addesp.com/wp-content/uploads/2020/09/1-1.webp" alt="" class="wp-image-111"/></figure>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="651" height="651" src="https://www.addesp.com/wp-content/uploads/2020/09/2-1.webp" alt="" class="wp-image-112"/></figure>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="651" height="631" src="https://www.addesp.com/wp-content/uploads/2020/09/3-1.webp" alt="" class="wp-image-113"/></figure>



<h3 class="wp-block-heading">如果挖矿难度过高</h3>



<p class="wp-block-paragraph">这个就很容易理解了，出块速度过慢则会导致整个比特币系统十分低效，也会打击矿工的积极性。比如我发布一笔交易可能要等一个月甚至更久才会被确认，这太慢了。</p>



<h2 class="wp-block-heading">如何调整挖矿难度</h2>



<p class="wp-block-paragraph">比特币系统会在每产生2016个区块后调整难度，下面是调整公式。</p>



<p class="wp-block-paragraph"><em>中本聪也没有说明为何是每2016个区块才调整难度，可能是他/她喜欢。</em></p>



<p class="wp-block-paragraph">难度调整的公式如下</p>



<p class="wp-block-paragraph">\[&nbsp;&nbsp;target=target&nbsp;\times&nbsp;\frac{ActualTime}{ExpectTime} \]</p>



<p class="wp-block-paragraph">调整难度实际上就是调整target值，target&nbsp;越小则难度越大，反之则难度越小。</p>



<p class="wp-block-paragraph">同时为了保证挖矿难度不会出现大幅度的波动，一旦$\frac{ActualTime}{ExpectTime}&nbsp;&lt;&nbsp;\frac{1}{4}$，则一律按照等于$\frac{1}{4}$计算，一旦$\frac{ActualTime}{ExpectTime}&nbsp;&gt;&nbsp;4$，则一律按照等于$4$计算，保证挖矿难度不会有大幅度变动。</p>



<h2 class="wp-block-heading">参考资料</h2>



<ul class="wp-block-list"><li><a href="https://www.bilibili.com/video/BV1Vt411X7JF" data-type="URL" data-id="https://www.bilibili.com/video/BV1Vt411X7JF" target="_blank" rel="noreferrer noopener">北京大学肖臻老师《区块链技术与应用》公开课</a></li></ul>
]]></content:encoded>
					
					<wfw:commentRss>https://www.addesp.com/archives/109/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>比特币的网络</title>
		<link>https://www.addesp.com/archives/134</link>
					<comments>https://www.addesp.com/archives/134#respond</comments>
		
		<dc:creator><![CDATA[ADD-SP]]></dc:creator>
		<pubDate>Wed, 13 May 2020 13:58:06 +0000</pubDate>
				<category><![CDATA[比特币]]></category>
		<guid isPermaLink="false">http://69a274df.addesp.com/?p=134</guid>

					<description><![CDATA[P2P Overlay network &#038; 交易和区块的广播]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">概述</h2>



<ul class="wp-block-list"><li>P2P&nbsp;Overlay&nbsp;network</li><li>交易的广播</li><li>区块的广播</li></ul>



<h2 class="wp-block-heading">P2P&nbsp;Overlay&nbsp;network</h2>



<p class="wp-block-paragraph">比特币网络的设计目标是简单、鲁棒，而非高效。<br>比特币的网络层运行的是一个&nbsp;P2P&nbsp;Overlay&nbsp;network，网络中所有的节点都是对等的，使用TCP协议通信。</p>



<h3 class="wp-block-heading">如何发现其它节点</h3>



<p class="wp-block-paragraph">既然每个网络节点都是平等的（是指在网络层面上节点是平等的，但各节点在功能上可以有不同的分工，&nbsp;如钱包节点、挖矿节点等），不存在任何“特殊”中心节点，那么当新的网络节点启动后，它是如何跟其他的节点建立连接，从而加入到比特币网络呢？</p>



<p class="wp-block-paragraph">在中心化网络中，新加入的节点只要连接“特殊”的中心节点就可以加入网络。</p>



<p class="wp-block-paragraph">为了能够加入到比特币网络，比特币客户端会做一下几件事情：</p>



<ol class="wp-block-list"><li>节点会记住它最近成功连接的网络节点，当重新启动后它可以迅速与先前的对等节点网络重新建立连接。</li><li>节点会在失去已有连接时尝试发现新节点。</li><li>当建立一个或多个连接后，节点将一条包含自身&nbsp;IP&nbsp;地址消息发送给其相邻节点。相邻节点再将此消息依次转发给它们各自的相邻节点，从而保证节点信息被多个节点所接收、保证连接更稳定。</li><li>新接入的节点可以向它的相邻节点发送获取地址&nbsp;getaddr&nbsp;消息，要求它们返回其已知对等节点的&nbsp;IP&nbsp;地址列表。节点可以找到需连接到的对等节点。</li><li>在节点启动时，可以给节点指定一个正活跃节点&nbsp;IP,&nbsp;如果没有，客户端也维持一个列表，列出了那些长期稳定运行的节点。这样的节点也被称为种子节点（其实和&nbsp;BT&nbsp;下载的种子文件道理是一样的），就可以通过种子节点来快速发现网络中的其他节点。</li></ol>



<p class="wp-block-paragraph"><em>相邻节点的选择是无视底层网络拓扑的，一个在A国的节点的相邻节点可能会在B国，并不是空间上的相邻。</em></p>



<h2 class="wp-block-heading">交易的广播</h2>



<p class="wp-block-paragraph">当一个节点收到一笔合法交易的时候会记住这笔交易并广播给相邻的节点，通过&nbsp;Flooding&nbsp;来将一个交易广播给全网，同时当节点收到重复的交易时也自然不会重复广播。<br>如果某个交易被写到区块链中后，则之前记住这笔交易的节点就要删除这个交易，因为已经没有必要记录了。<br>如果节点收到的被写到区块链中的交易和自己记住的交易发生冲突，则删除自己记录的交易。</p>



<h2 class="wp-block-heading">区块的广播</h2>



<p class="wp-block-paragraph">广播的过程和交易类似，区别是收到区块的节点还要判断这个区块是不是在最长合法链内。</p>



<h2 class="wp-block-heading">参考资料</h2>



<ul class="wp-block-list"><li><a rel="noreferrer noopener" href="https://learnblockchain.cn/2017/11/07/bitcoin-p2p/" data-type="URL" data-id="https://learnblockchain.cn/2017/11/07/bitcoin-p2p/" target="_blank">分析比特币网络：一种去中心化、点对点的网络架构</a></li><li><a href="https://www.bilibili.com/video/BV1Vt411X7JF" data-type="URL" data-id="https://www.bilibili.com/video/BV1Vt411X7JF" target="_blank" rel="noreferrer noopener">北京大学肖臻老师《区块链技术与应用》公开课</a></li></ul>
]]></content:encoded>
					
					<wfw:commentRss>https://www.addesp.com/archives/134/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>比特币的实现</title>
		<link>https://www.addesp.com/archives/127</link>
					<comments>https://www.addesp.com/archives/127#respond</comments>
		
		<dc:creator><![CDATA[ADD-SP]]></dc:creator>
		<pubDate>Wed, 13 May 2020 13:17:30 +0000</pubDate>
				<category><![CDATA[比特币]]></category>
		<guid isPermaLink="false">http://69a274df.addesp.com/?p=127</guid>

					<description><![CDATA[UTXO &#038; 出块概率 &#038; 交易确认]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">概述</h2>



<ul class="wp-block-list"><li>Unspent&nbsp;Transaction&nbsp;Output（UTXO）</li><li>出块概率</li><li>交易的确认</li></ul>



<h2 class="wp-block-heading">Unspent&nbsp;Transaction&nbsp;Output（UTXO）</h2>



<p class="wp-block-paragraph">UTXO 是 BTC 交易的基本单位，也就是说其不可分割，每一笔交易都会产生新的UTXO，一个 UTXO 可以包含的BTC可以是一聪的任意整数倍。</p>



<p class="wp-block-paragraph"><em>聪：BTC 的基本单位。</em></p>



<p class="wp-block-paragraph">Full&nbsp;node需要在内存中维护UTXO以保证可以快速检测Double&nbsp;spending&nbsp;attack。</p>



<p class="wp-block-paragraph">除了coinbase交易以外，任何一笔交易都会有一个输入和若干个输出，$输入的BTC=输出的BTC+手续费$。</p>



<p class="wp-block-paragraph">张三和李四拥有的UTXO如下：</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="475" height="324" src="https://www.addesp.com/wp-content/uploads/2020/09/0-3.webp" alt="" class="wp-image-129"/></figure>



<p class="wp-block-paragraph">现在张三要给李四转 4 个 BTC，首先张三拥有的 UTXO 中没有正好包含 4 个 BTC 的 UTXO，但是张三可以选择将包含 5 个 BTC 的 UTXO，转账给李四 4 个 BTC，然后转账给自己 1 个 BTC，就完成了一笔交易。本地交易的输入为张三持有的包含 5 个 BTC 的 UTXO，输入给下图中绿色的 UTXO。</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="475" height="324" src="https://www.addesp.com/wp-content/uploads/2020/09/1-3.webp" alt="" class="wp-image-130"/></figure>



<p class="wp-block-paragraph">我们称这种模式为 Transactor-base&nbsp;ledger。</p>



<h2 class="wp-block-heading">出块概率</h2>



<p class="wp-block-paragraph">挖矿的过程就是不断尝试各种&nbsp;nonce&nbsp;来求解&nbsp;puzzle，每次尝试&nbsp;nonce&nbsp;都可以看作一个&nbsp;Bernoulli&nbsp;trial：a&nbsp;random&nbsp;experiment&nbsp;with&nbsp;binary&nbsp;outcome（伯努利试验：一个二元结果的随机试验）。伯努利实验的经典案例是抛硬币，如果硬币不均匀，每次实验，正面的概率就是&nbsp;p，反面的概率是&nbsp;1-p。对于挖矿来说，这两个概率相差甚远，每次尝试一个nonce，成功的概率是微乎其微的，大概率不行的，如果我们做大量的伯努利实验，每个实验都是随机的，那么这些伯努利实验就构成了一个&nbsp;Bernoulli&nbsp;process：a&nbsp;sequence&nbsp;of&nbsp;independent&nbsp;Bernoulli&nbsp;trial（伯努利过程：一系列独立伯努利试验）。Bernoulli&nbsp;process&nbsp;的一个特性是无记忆性（memoryless），意思是说做大量实验，前面的实验结果对后面的结果没有影响，挖矿过程就是不断尝试nonce，这种情况下&nbsp;&nbsp;&nbsp;Bernoulli&nbsp;process可以用&nbsp;Poisson&nbsp;process&nbsp;来近似，实验次数很多，每次成功的概率很小就可以用&nbsp;Poisson&nbsp;process&nbsp;来近似。我们关心的是出块时间，也就是系统里产生下一个区块的时间，这个在概率上可以推导出来，服从指数分布。</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="715" height="286" src="https://www.addesp.com/wp-content/uploads/2020/09/2-3.webp" alt="" class="wp-image-131"/></figure>



<p class="wp-block-paragraph">注意，上图的出块时间是整个系统的出块时间，并不是矿工的出块时间，整个系统的平均出块时间是10分钟，平均时间是比特币协议规定的，通过定期调整挖矿难度，使得平均出块时间维持在10分钟左右，具体到每一个矿工，出块时间取决于矿工的算力，算力大，概率就大，出块时间就短。指数分布也是无记忆的，概率密度曲线的特点有：从任何一个地方把它截断，剩下曲线的形状仍然和原来一样，仍然服从指数分布，这就是无记忆的性质，假如过去了10分钟，仍然没有人找到区块，那么接下来还要等多久呢？还是平均下来10分钟，这个和直觉不太一致。这个概率分析告诉我们将来还要挖多少时间和过去挖了多少时间是没有关系的，仍然是服从指数分布，平均还要10分钟，这个性质也叫&nbsp;Progress&nbsp;free。</p>



<h3 class="wp-block-heading">Progress&nbsp;free&nbsp;或者&nbsp;Memoryless&nbsp;有什么作用？</h3>



<p class="wp-block-paragraph">设想一下，如果有某个&nbsp;puzzle&nbsp;不满足这个性质会出现什么情况，比如说过去做的工作越多，接下来尝试&nbsp;nonce&nbsp;的时候成功的概率越大，相当于抛硬币的时候每次结果不是随机的，过去抛了好多次，都是反面朝上，下次再抛硬币的时候，正面朝上的概率会增加。如果有某一个加密货币设计出这样的&nbsp;puzzle，会有什么结果？算力强的矿工会有不成比例的优势，因为算力强的矿工过去的工作量更大。什么是不成比例的优势？比如系统中有两个矿工，一个的算力是另一个的10倍，理想状况下，算力强的矿工能挖到矿的概率应该是另一个矿工的10倍，这才算公平，因为算力强的矿工能尝试的nonce是另一个的10倍，这就是我们说的Progress&nbsp;free&nbsp;或者&nbsp;Memoryless&nbsp;所保证的，如果不是这样的话，算力强的矿工获得记账权的概率就会超过10倍，因为它过去尝试了更多的不成功&nbsp;nonce，那么下次成功的概率就会增大，这就是不成比例的优势。所以&nbsp;Progress&nbsp;free&nbsp;或者&nbsp;Memoryless&nbsp;保证了挖矿公平性。</p>



<h2 class="wp-block-heading">交易的确认</h2>



<p class="wp-block-paragraph">当一笔交易刚刚被写到区块中的时候交易并不会完全被确认的，此时是&nbsp;One&nbsp;confirmation，此时可能会出现分叉攻击，即在前一个区块后面发布一个不相同的区块来造成分叉，如果攻击者的链条称为最长合法链后则这笔交易就不在最长合法链中，交易会被回滚。</p>



<p class="wp-block-paragraph"><br>解决的方法是交易的确认需要&nbsp;Six&nbsp;confirmation。此时攻击者发起的分叉攻击就几乎不可能追赶上六个区块了，除非这个攻击者拥有全网半数以上的算力，这种攻击也称「51%算力攻击」。</p>



<h2 class="wp-block-heading">参考资料</h2>



<ul class="wp-block-list"><li><a rel="noreferrer noopener" href="https://www.mscto.com/blockchain/258341.html" data-type="URL" data-id="https://www.mscto.com/blockchain/258341.html" target="_blank">比特币实现</a></li><li><a href="https://www.bilibili.com/video/BV1Vt411X7JF" data-type="URL" data-id="https://www.bilibili.com/video/BV1Vt411X7JF" target="_blank" rel="noreferrer noopener">北京大学肖臻老师《区块链技术与应用》公开课</a></li></ul>
]]></content:encoded>
					
					<wfw:commentRss>https://www.addesp.com/archives/127/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>区块链的密码学原理</title>
		<link>https://www.addesp.com/archives/158</link>
					<comments>https://www.addesp.com/archives/158#respond</comments>
		
		<dc:creator><![CDATA[ADD-SP]]></dc:creator>
		<pubDate>Mon, 04 May 2020 14:32:46 +0000</pubDate>
				<category><![CDATA[区块链]]></category>
		<guid isPermaLink="false">http://69a274df.addesp.com/?p=158</guid>

					<description><![CDATA[Cryptographic hash function &#038; Signature]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">概述</h2>



<ul class="wp-block-list"><li>Cryptographic&nbsp;hash&nbsp;function</li><li>Signature</li></ul>



<h2 class="wp-block-heading">Cryptographic&nbsp;hash&nbsp;function</h2>



<p class="wp-block-paragraph">它有三个重要性质，分别为&nbsp;Collision&nbsp;resistance、Hiding&nbsp;和&nbsp;Puzzle&nbsp;friendly。</p>



<ul class="wp-block-list"><li>Collision&nbsp;resistance：不能找到一个快捷的方法使得$Hash(x_0)=Hash(x_1)=y&nbsp;且&nbsp;x_0&nbsp;\neq&nbsp;x_1$。</li><li>Hiding：假如$Hash(x)=y$，那么不能找到一个快捷的方法从$y$反推出$x$，前提是输入空间足够大且取值均匀，这时可以通过在$x$中增加一个随机数来解决这个问题。</li><li>Puzzle&nbsp;friendly：哈希函数的输出结果是难以预测的，即难以通过哈希函数的输入来预测哈希函数的输出结果。</li></ul>



<h2 class="wp-block-heading">Signature</h2>



<p class="wp-block-paragraph">它依赖于<a rel="noreferrer noopener" href="https://baike.baidu.com/item/%E9%9D%9E%E5%AF%B9%E7%A7%B0%E5%8A%A0%E5%AF%86%E7%AE%97%E6%B3%95" data-type="URL" data-id="https://baike.baidu.com/item/%E9%9D%9E%E5%AF%B9%E7%A7%B0%E5%8A%A0%E5%AF%86%E7%AE%97%E6%B3%95" target="_blank">Asymmetric&nbsp;cryptographic&nbsp;algorithm</a>，生成一对公私钥，私钥加密公钥解密，私钥签名公钥验证，所以公钥是可以公开的。</p>



<h2 class="wp-block-heading">参考资料</h2>



<ul class="wp-block-list"><li><a href="https://www.bilibili.com/video/BV1Vt411X7JF" data-type="URL" data-id="https://www.bilibili.com/video/BV1Vt411X7JF" target="_blank" rel="noreferrer noopener">北京大学肖臻老师《区块链技术与应用》公开课</a></li></ul>
]]></content:encoded>
					
					<wfw:commentRss>https://www.addesp.com/archives/158/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>比特币的协议</title>
		<link>https://www.addesp.com/archives/141</link>
					<comments>https://www.addesp.com/archives/141#respond</comments>
		
		<dc:creator><![CDATA[ADD-SP]]></dc:creator>
		<pubDate>Mon, 04 May 2020 13:03:24 +0000</pubDate>
				<category><![CDATA[比特币]]></category>
		<guid isPermaLink="false">http://69a274df.addesp.com/?p=141</guid>

					<description><![CDATA[Block 的结构 &#038; 节点分类 &#038; 比特币的来源 &#038; 分布式共识]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">概述</h2>



<ul class="wp-block-list"><li>Block 的结构</li><li>节点类型</li><li>谁可以发布比特币</li><li>如何验证交易的有效性（分布式共识）</li></ul>



<h2 class="wp-block-heading">Block 的结构</h2>



<h3 class="wp-block-heading">Header</h3>



<ul class="wp-block-list"><li>version</li><li>hash&nbsp;of&nbsp;previours&nbsp;block&nbsp;header</li><li>merkle&nbsp;root&nbsp;hash</li><li>target</li><li>nonce</li></ul>



<p class="wp-block-paragraph">挖矿的过程就是寻找一个&nbsp;nonce&nbsp;使得$Hash(Header)&nbsp;\leq&nbsp;target$。</p>



<h3 class="wp-block-heading">Body</h3>



<ul class="wp-block-list"><li>transcation&nbsp;list</li></ul>



<h2 class="wp-block-heading">节点类型</h2>



<h3 class="wp-block-heading">Full&nbsp;node</h3>



<ul class="wp-block-list"><li>一直在线</li><li>在本地硬盘上维护完整的区块链信息</li><li>在内存中维护 UTXO 集合以便快速检测交易的合法性</li><li>监听比特币网络上的交易信息并验证每笔交易的合法性</li><li>决定哪些交易会被打包到区块里</li><li>监听别的矿工挖出来的区块并验证其合法性</li><li>挖矿<ul><li>决定沿着哪条链挖下去</li><li>当出现等长的分叉时选择哪一个分叉（缺省状态下选择先监听到的分叉）</li></ul></li></ul>



<h3 class="wp-block-heading">Light&nbsp;node</h3>



<ul class="wp-block-list"><li>无需一直在线</li><li>不保存整个区块链而只保存每个区块的&nbsp;Header</li><li>不保存全部的交易而只保存与自己相关的交易</li><li>无法检验大多数交易的合法性而只能检验和自己相关的交易的合法性</li><li>无法检测系统中发布的区块的正确性</li><li>可以验证挖矿难度</li><li>只能检测哪个是最长链而不知道哪个是最长合法链</li></ul>



<h2 class="wp-block-heading">谁可以发布比特币</h2>



<p class="wp-block-paragraph">比特币系统中<strong>唯一</strong>可以发布比特币的途径就是区块被接受后获得的奖励。奖励初始为&nbsp;\\(50BTC\\)，每接受21万个区块后奖励减半。<br>当奖励已经很少的时候获取BTC主要是靠交易的手续费。</p>



<h2 class="wp-block-heading">如何验证交易的有效性（分布式共识）</h2>



<h3 class="wp-block-heading">获取记账权</h3>



<p class="wp-block-paragraph">挖矿的过程就是寻找一个&nbsp;nonce&nbsp;使得$Hash(Header)&nbsp;\leq&nbsp;target$，优先完成这个工作的节点就获得了这个区块的记账权。节点无法欺骗其它节点说自己完成了工作，因为其它节点可以轻松地验证上述条件是否成立。</p>



<h3 class="wp-block-heading">接受区块&gt;</h3>



<ol class="wp-block-list"><li>验证区块的信息，如难度是否正确，交易信息是否没有篡改等。</li><li>通过查看 Header 确定当前区块是否在扩展最长链，如果是扩展最长链则接受该区块，反之则不接受。</li><li>下列情况下可能会导致几乎同时出现两个最长的链<ul><li>两个人几乎同时取得了记账权。</li><li>由于网络等原因一部分节点接受了一个区块，另一部分节点接受了另一个区块，以及类似的情况。</li><li>其它情况。</li></ul></li><li>出现这种情况的时候接受哪条链实际上是这些链各个对应的矿工的算力比拼，如果其中一条链的矿工的算力可以让该链最快地扩展，则最终该链会被其它节点接受成为最长链。</li></ol>



<h3 class="wp-block-heading">激励机制</h3>



<p class="wp-block-paragraph">那么凭什么让矿工来拿出算力来维持这个共识？</p>



<ul class="wp-block-list"><li>一旦一个区块被接受，则拥有者可以获得奖励。奖励初始为50 BTC&nbsp;，每接受 21 万个区块后奖励减半。这在奖励比较多的时候可以大大激发矿工的挖矿热情。</li><li>一个区块是可以记账的，每当一笔交易被记录到一个区块内的时候，区块的拥有者就可以收取一定手续费。</li></ul>



<h2 class="wp-block-heading">参考资料</h2>



<ul class="wp-block-list"><li><a href="https://www.bilibili.com/video/BV1Vt411X7JF" data-type="URL" data-id="https://www.bilibili.com/video/BV1Vt411X7JF" target="_blank" rel="noreferrer noopener">北京大学肖臻老师《区块链技术与应用》公开课</a></li></ul>
]]></content:encoded>
					
					<wfw:commentRss>https://www.addesp.com/archives/141/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>ARM-MDK 工程模板建立（基于标准固件库）</title>
		<link>https://www.addesp.com/archives/450</link>
					<comments>https://www.addesp.com/archives/450#respond</comments>
		
		<dc:creator><![CDATA[ADD-SP]]></dc:creator>
		<pubDate>Fri, 10 Apr 2020 00:41:02 +0000</pubDate>
				<category><![CDATA[笔记]]></category>
		<category><![CDATA[ARM]]></category>
		<category><![CDATA[Keil]]></category>
		<category><![CDATA[MDK]]></category>
		<guid isPermaLink="false">https://www.addesp.com/?p=450</guid>

					<description><![CDATA[介绍如何建立STM32F10X的MDK工程模板（基于标准固件库）。]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading"><strong>概述</strong></h2>



<p class="wp-block-paragraph">介绍如何建立STM32F10X的MDK工程模板（基于标准固件库）。</p>



<h2 class="wp-block-heading"><strong>正文</strong></h2>



<ol class="wp-block-list"><li>新建一个文件夹命名为&nbsp;Template。</li><li>在文件夹下新建三个文件夹，分别为，core，stdlib，user。</li><li>前往<a rel="noreferrer noopener" href="https://www.stmicroelectronics.com.cn/zh/embedded-software/stm32-standard-peripheral-libraries.html" data-type="URL" data-id="https://www.stmicroelectronics.com.cn/zh/embedded-software/stm32-standard-peripheral-libraries.html" target="_blank">官网</a>下载标准固件库。</li><li>拷贝 Libraries\CMSIS\CM3\CoreSupport 下的所有文件到 Template\core。</li><li>拷贝 Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x\startup\arm 下对应的启动文件到 Template\core 下。</li><li>拷贝 Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x 下的 stm32f10x.h，system_stm32f10x.c，system_stm32f10x.h到 Template\core 下。</li><li>拷贝 Libraries\STM32F10x_StdPeriph_Driver 下的 inc 和 src 两个文件夹到  Template\stdlib 下。</li><li>拷贝 Project\STM32F10x_StdPeriph_Template 下的 main.c ，stm32f10x_conf.h，stm32f10x_it.c，stm32f10x_it.h，system_stm32f10x.c 到 Template\user 下。</li><li>用&nbsp;MDK&nbsp;新建工程到 Template\user 目录下。</li><li><ul><li>添加宏 USE_STDPERIPH_DRIVER。</li><li>根据<a rel="noreferrer noopener" href="https://blog.csdn.net/u013385925/article/details/51165541" data-type="URL" data-id="https://blog.csdn.net/u013385925/article/details/51165541" target="_blank">STM32&nbsp;&#8211;&nbsp;Keil中启动文件的选择和固件库宏定义</a>选择对应的宏如STM32F103R6就填入`STM32F10X_HD`（<strong>如果后续编译出现问题可以移除该宏重试</strong>）。</li><li>多个宏之间用半角逗号分隔。</li><li><img loading="lazy" decoding="async" width="150" height="113" class="wp-image-452" style="width: 150px;" src="https://www.addesp.com/wp-content/uploads/2020/09/0-15.webp" alt=""></li></ul></li><li>添加下列头文件路径<ul><li>添加 Template\core。</li><li>添加 Template\stdlib\inc。</li><li>添加 Template\user。</li><li><img loading="lazy" decoding="async" width="150" height="113" class="wp-image-453" style="width: 150px;" src="https://www.addesp.com/wp-content/uploads/2020/09/1-13.webp" alt=""></li></ul></li><li>添加下列分组并向分组中添加对应的文件<ul><li>core 分组中添加 Template\core 下所有的 *.c，*.s 文件。</li><li>stdlib 分组添加 Template\stdlib\inc 下所有的 *.c 文件。</li><li>user 分组添加 Template\user 下所有的 *.c 文件。</li><li><img loading="lazy" decoding="async" width="150" height="113" class="wp-image-455" style="width: 150px;" src="https://www.addesp.com/wp-content/uploads/2020/09/2-13.webp" alt=""></li></ul></li><li>清空 main。c 中的内容，写一个空的 int&nbsp;main(void) 函数进去。</li><li>编译工程，见招拆招。</li></ol>



<p class="wp-block-paragraph"></p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.addesp.com/archives/450/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>区块链的数据结构</title>
		<link>https://www.addesp.com/archives/150</link>
					<comments>https://www.addesp.com/archives/150#respond</comments>
		
		<dc:creator><![CDATA[ADD-SP]]></dc:creator>
		<pubDate>Sat, 04 Apr 2020 04:51:44 +0000</pubDate>
				<category><![CDATA[区块链]]></category>
		<guid isPermaLink="false">http://69a274df.addesp.com/?p=150</guid>

					<description><![CDATA[Blockchain &#038; Merkle Tree]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">概述</h2>



<ul class="wp-block-list"><li>Blockchain</li><li>Merkle&nbsp;Tree</li></ul>



<h2 class="wp-block-heading">Blockchain</h2>



<p class="wp-block-paragraph">本质上是链表，只不过传统的链表的指针域存储的下一个节点的地址，Blockchain&nbsp;的指针域则存储上一个节点的 Hash。</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="682" height="112" src="https://www.addesp.com/wp-content/uploads/2020/09/0-4.webp" alt="" class="wp-image-151"/></figure>



<p class="wp-block-paragraph">结构如上图，如果从左到右第二个区块被篡改。</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="702" height="132" src="https://www.addesp.com/wp-content/uploads/2020/09/1-4.webp" alt="" class="wp-image-152"/></figure>



<p class="wp-block-paragraph">因此后续的节点都使用的是哈希指针，所以必然会导致右边的两个区块的哈希指针发生改变。</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="702" height="132" src="https://www.addesp.com/wp-content/uploads/2020/09/2-4.webp" alt="" class="wp-image-153"/></figure>



<p class="wp-block-paragraph">所以系统只需要保存最后一个区块的 Hash &nbsp;就可以检验整个区块链是否被篡改。<br>有了这些性质后节点就没有必要保存所有的区块，当需要其它区块的时候，可以向其它节点请求，得到之后通过验证 Hash 就可以知道这个区块是否被篡改过。</p>



<h2 class="wp-block-heading">Merkle&nbsp;Tree</h2>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1559" height="638" src="https://www.addesp.com/wp-content/uploads/2020/09/3-3.webp" alt="" class="wp-image-154"/></figure>



<p class="wp-block-paragraph">Blockchain的header中只需要保存root&nbsp;hash即可，这样对Blockchain取哈希的时候也只需要对header取哈希就行了，也能够起到防篡改作用。<br>那么假如我想知道某个block是否已经写在了Merkle&nbsp;Tree里，则可以向其它节点请求下图中紫色的 Hash，然后通过计算 Hash&nbsp;就可以验证。</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1559" height="638" src="https://www.addesp.com/wp-content/uploads/2020/09/4-1.webp" alt="" class="wp-image-155"/></figure>



<p class="wp-block-paragraph">理论上恶意节点可以通过篡改某个区块的内容且使其 Hash&nbsp;不变的情况下来欺骗其它节点，但目前的实际应用中是不可能的，因为这是在人为制造哈希碰撞，Collision&nbsp;resistance&nbsp;可以很好地解决这个问题。</p>



<p class="wp-block-paragraph">假如我想知道某个&nbsp;block&nbsp;是否不存在于&nbsp;Merkle&nbsp;Tree&nbsp;里，如果对叶结点的顺序没有特殊要求则需要遍历所有&nbsp;block，但是如果&nbsp;block经过排序，则可以在对数时间内进行验证。</p>



<h2 class="wp-block-heading">参考资料</h2>



<ul class="wp-block-list"><li><a href="https://www.bilibili.com/video/BV1Vt411X7JF" data-type="URL" data-id="https://www.bilibili.com/video/BV1Vt411X7JF" target="_blank" rel="noreferrer noopener">北京大学肖臻老师《区块链技术与应用》公开课</a></li></ul>
]]></content:encoded>
					
					<wfw:commentRss>https://www.addesp.com/archives/150/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
