<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Aulee</title>
        <link>https://paragraph.com/@aulee</link>
        <description>Graduated as a PhD of Business Administration (Finance), developing into an independent researcher of cryptos, DeFi,  and monetary economics</description>
        <lastBuildDate>Wed, 15 Apr 2026 07:20:06 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <image>
            <title>Aulee</title>
            <url>https://storage.googleapis.com/papyrus_images/0dea453d2fe8de02d688ea18586484f084c9fae060a27dc8aa218596a3fc0c00.png</url>
            <link>https://paragraph.com/@aulee</link>
        </image>
        <copyright>All rights reserved</copyright>
        <item>
            <title><![CDATA[Linux Network Commands]]></title>
            <link>https://paragraph.com/@aulee/linux-network-commands</link>
            <guid>LgqOyxmgsi3PyfM400jK</guid>
            <pubDate>Fri, 10 Jan 2025 03:37:21 GMT</pubDate>
            <description><![CDATA[Routingnetstat -rn Legacy command for network status, show routes, and numeric displayroute -n a route command all to itself Instead, now we have the ip set of commands.OrIP addressip address and for IP version 4 (IPv4)ip -4 address Network Manager Command Linesudo nmcli connection show]]></description>
            <content:encoded><![CDATA[<h3 id="h-routing" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Routing</h3><pre data-type="codeBlock" text="netstat -rn
"><code>netstat <span class="hljs-operator">-</span>rn
</code></pre><p>Legacy command for network status, show routes, and numeric display</p><pre data-type="codeBlock" text="route -n
"><code>route <span class="hljs-operator">-</span>n
</code></pre><p>a <code>route</code> command all to itself</p><p>Instead, now we have the <code>ip</code> set of commands.</p><pre data-type="codeBlock" text="ip route
"><code></code></pre><p>Or</p><pre data-type="codeBlock" text="ip r
"><code></code></pre><h3 id="h-ip-address" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">IP address</h3><pre data-type="codeBlock" text="ip address
"><code>ip <span class="hljs-selector-tag">address</span>
</code></pre><p>and for IP version 4 (IPv4)</p><pre data-type="codeBlock" text="ip -4 address
"><code>ip -<span class="hljs-number">4</span> <span class="hljs-selector-tag">address</span>
</code></pre><h3 id="h-network-manager-command-line" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Network Manager Command Line</h3><pre data-type="codeBlock" text="sudo nmcli connection show
"><code>sudo nmcli connection <span class="hljs-keyword">show</span>
</code></pre>]]></content:encoded>
            <author>aulee@newsletter.paragraph.com (Aulee)</author>
        </item>
        <item>
            <title><![CDATA[Fibonacci数列的和式表达及wat实现]]></title>
            <link>https://paragraph.com/@aulee/fibonacci-wat</link>
            <guid>7I6a7g9alNcjyWjWkcN7</guid>
            <pubDate>Thu, 29 Aug 2024 09:30:21 GMT</pubDate>
            <description><![CDATA[设数列的第$$n$$项为$$f(n)$$，则根据定义有$$f(n)=f(n-1)+f(n-2)$$。由此易得， $$f(n) = f(1) + f(0) + f(1) + … + f(n-2)$$ 用wat语言（wasm）可实现求和法计算$$f(n)$$如下：(module (func $fib (export "fib") (param $n i32) (result i32) (local $tmp i32) (local.set $tmp (i32.const 1)) (block $break (br_if $break (i32.lt_s (local.get $n) (i32.const 2))) (loop $loop (local.set $tmp (i32.add (call $fib (i32.add (local.get $n) (i32.const -2))) (local.get $tmp))) (br_if $loop (i32.gt_s (local.tee $n (i32.add (local.get $n) (i32.const -1))) (i...]]></description>
            <content:encoded><![CDATA[<p>设数列的第$$n$$项为$$f(n)$$，则根据定义有$$f(n)=f(n-1)+f(n-2)$$。由此易得，</p><p>$$f(n) = f(1) + f(0) + f(1) + … + f(n-2)$$</p><p>用wat语言（wasm）可实现求和法计算$$f(n)$$如下：</p><pre data-type="codeBlock" text="(module
    (func $fib (export &quot;fib&quot;) (param $n i32) (result i32)
        (local $tmp i32)
        
        (local.set $tmp (i32.const 1))
        
        (block $break
            (br_if $break (i32.lt_s
                                (local.get $n)
                                (i32.const 2)))
            (loop $loop
                (local.set $tmp (i32.add
                                    (call $fib (i32.add
                                                    (local.get $n)
                                                    (i32.const -2)))
                                    (local.get $tmp)))
                (br_if $loop (i32.gt_s
                                (local.tee $n (i32.add (local.get $n) (i32.const -1)))
                                (i32.const 1)))
            )
        )

        local.get $tmp        

    )
)

直接用f(n)=f(n-1)+f(n-2)的逻辑，wat代码为：
(module
    (func $fib (export &quot;fib&quot;) (param $n i32) (result i32)
        (local $tmp i32)
        
        (local.set $tmp (i32.const 1))
        
        (block $break
            (br_if $break (i32.lt_s
                                (local.get $n)
                                (i32.const 2)))
            (local.set $tmp (i32.add
                                    (call $fib (i32.add
                                                    (local.get $n)
                                                    (i32.const -2)))
                                    (call $fib (i32.add
                                                    (local.get $n)
                                                    (i32.const -1)))))
        )
        local.get $tmp        
    )
)
"><code><span class="hljs-punctuation">(</span><span class="hljs-keyword">module</span>
    <span class="hljs-punctuation">(</span><span class="hljs-keyword">func</span> <span class="hljs-title function_">$fib</span> <span class="hljs-punctuation">(</span><span class="hljs-keyword">export</span> <span class="hljs-string">"fib"</span><span class="hljs-punctuation">)</span> <span class="hljs-punctuation">(</span><span class="hljs-keyword">param</span> <span class="hljs-variable">$n</span> <span class="hljs-type">i32</span><span class="hljs-punctuation">)</span> <span class="hljs-punctuation">(</span><span class="hljs-keyword">result</span> <span class="hljs-type">i32</span><span class="hljs-punctuation">)</span>
        <span class="hljs-punctuation">(</span><span class="hljs-keyword">local</span> <span class="hljs-variable">$tmp</span> <span class="hljs-type">i32</span><span class="hljs-punctuation">)</span>
        
        <span class="hljs-punctuation">(</span><span class="hljs-keyword">local.set</span> <span class="hljs-variable">$tmp</span> <span class="hljs-punctuation">(</span><span class="hljs-keyword">i32.const</span> <span class="hljs-number">1</span><span class="hljs-punctuation">))</span>
        
        <span class="hljs-punctuation">(</span><span class="hljs-keyword">block</span> <span class="hljs-variable">$break</span>
            <span class="hljs-punctuation">(</span><span class="hljs-keyword">br_if</span> <span class="hljs-variable">$break</span> <span class="hljs-punctuation">(</span><span class="hljs-keyword">i32.lt_s</span>
                                <span class="hljs-punctuation">(</span><span class="hljs-keyword">local.get</span> <span class="hljs-variable">$n</span><span class="hljs-punctuation">)</span>
                                <span class="hljs-punctuation">(</span><span class="hljs-keyword">i32.const</span> <span class="hljs-number">2</span><span class="hljs-punctuation">)))</span>
            <span class="hljs-punctuation">(</span><span class="hljs-keyword">loop</span> <span class="hljs-variable">$loop</span>
                <span class="hljs-punctuation">(</span><span class="hljs-keyword">local.set</span> <span class="hljs-variable">$tmp</span> <span class="hljs-punctuation">(</span><span class="hljs-keyword">i32.add</span>
                                    <span class="hljs-punctuation">(</span><span class="hljs-keyword">call</span> <span class="hljs-title function_">$fib</span> <span class="hljs-punctuation">(</span><span class="hljs-keyword">i32.add</span>
                                                    <span class="hljs-punctuation">(</span><span class="hljs-keyword">local.get</span> <span class="hljs-variable">$n</span><span class="hljs-punctuation">)</span>
                                                    <span class="hljs-punctuation">(</span><span class="hljs-keyword">i32.const</span> <span class="hljs-number">-2</span><span class="hljs-punctuation">)))</span>
                                    <span class="hljs-punctuation">(</span><span class="hljs-keyword">local.get</span> <span class="hljs-variable">$tmp</span><span class="hljs-punctuation">)))</span>
                <span class="hljs-punctuation">(</span><span class="hljs-keyword">br_if</span> <span class="hljs-variable">$loop</span> <span class="hljs-punctuation">(</span><span class="hljs-keyword">i32.gt_s</span>
                                <span class="hljs-punctuation">(</span><span class="hljs-keyword">local.tee</span> <span class="hljs-variable">$n</span> <span class="hljs-punctuation">(</span><span class="hljs-keyword">i32.add</span> <span class="hljs-punctuation">(</span><span class="hljs-keyword">local.get</span> <span class="hljs-variable">$n</span><span class="hljs-punctuation">)</span> <span class="hljs-punctuation">(</span><span class="hljs-keyword">i32.const</span> <span class="hljs-number">-1</span><span class="hljs-punctuation">)))</span>
                                <span class="hljs-punctuation">(</span><span class="hljs-keyword">i32.const</span> <span class="hljs-number">1</span><span class="hljs-punctuation">)))</span>
            <span class="hljs-punctuation">)</span>
        <span class="hljs-punctuation">)</span>

        <span class="hljs-keyword">local.get</span> <span class="hljs-variable">$tmp</span>        

    <span class="hljs-punctuation">)</span>
<span class="hljs-punctuation">)</span>

直接用f<span class="hljs-punctuation">(</span>n<span class="hljs-punctuation">)</span>=f<span class="hljs-punctuation">(</span>n<span class="hljs-number">-1</span><span class="hljs-punctuation">)</span>+f<span class="hljs-punctuation">(</span>n<span class="hljs-number">-2</span><span class="hljs-punctuation">)</span>的逻辑，wat代码为：
<span class="hljs-punctuation">(</span><span class="hljs-keyword">module</span>
    <span class="hljs-punctuation">(</span><span class="hljs-keyword">func</span> <span class="hljs-title function_">$fib</span> <span class="hljs-punctuation">(</span><span class="hljs-keyword">export</span> <span class="hljs-string">"fib"</span><span class="hljs-punctuation">)</span> <span class="hljs-punctuation">(</span><span class="hljs-keyword">param</span> <span class="hljs-variable">$n</span> <span class="hljs-type">i32</span><span class="hljs-punctuation">)</span> <span class="hljs-punctuation">(</span><span class="hljs-keyword">result</span> <span class="hljs-type">i32</span><span class="hljs-punctuation">)</span>
        <span class="hljs-punctuation">(</span><span class="hljs-keyword">local</span> <span class="hljs-variable">$tmp</span> <span class="hljs-type">i32</span><span class="hljs-punctuation">)</span>
        
        <span class="hljs-punctuation">(</span><span class="hljs-keyword">local.set</span> <span class="hljs-variable">$tmp</span> <span class="hljs-punctuation">(</span><span class="hljs-keyword">i32.const</span> <span class="hljs-number">1</span><span class="hljs-punctuation">))</span>
        
        <span class="hljs-punctuation">(</span><span class="hljs-keyword">block</span> <span class="hljs-variable">$break</span>
            <span class="hljs-punctuation">(</span><span class="hljs-keyword">br_if</span> <span class="hljs-variable">$break</span> <span class="hljs-punctuation">(</span><span class="hljs-keyword">i32.lt_s</span>
                                <span class="hljs-punctuation">(</span><span class="hljs-keyword">local.get</span> <span class="hljs-variable">$n</span><span class="hljs-punctuation">)</span>
                                <span class="hljs-punctuation">(</span><span class="hljs-keyword">i32.const</span> <span class="hljs-number">2</span><span class="hljs-punctuation">)))</span>
            <span class="hljs-punctuation">(</span><span class="hljs-keyword">local.set</span> <span class="hljs-variable">$tmp</span> <span class="hljs-punctuation">(</span><span class="hljs-keyword">i32.add</span>
                                    <span class="hljs-punctuation">(</span><span class="hljs-keyword">call</span> <span class="hljs-title function_">$fib</span> <span class="hljs-punctuation">(</span><span class="hljs-keyword">i32.add</span>
                                                    <span class="hljs-punctuation">(</span><span class="hljs-keyword">local.get</span> <span class="hljs-variable">$n</span><span class="hljs-punctuation">)</span>
                                                    <span class="hljs-punctuation">(</span><span class="hljs-keyword">i32.const</span> <span class="hljs-number">-2</span><span class="hljs-punctuation">)))</span>
                                    <span class="hljs-punctuation">(</span><span class="hljs-keyword">call</span> <span class="hljs-title function_">$fib</span> <span class="hljs-punctuation">(</span><span class="hljs-keyword">i32.add</span>
                                                    <span class="hljs-punctuation">(</span><span class="hljs-keyword">local.get</span> <span class="hljs-variable">$n</span><span class="hljs-punctuation">)</span>
                                                    <span class="hljs-punctuation">(</span><span class="hljs-keyword">i32.const</span> <span class="hljs-number">-1</span><span class="hljs-punctuation">)))))</span>
        <span class="hljs-punctuation">)</span>
        <span class="hljs-keyword">local.get</span> <span class="hljs-variable">$tmp</span>        
    <span class="hljs-punctuation">)</span>
<span class="hljs-punctuation">)</span>
</code></pre>]]></content:encoded>
            <author>aulee@newsletter.paragraph.com (Aulee)</author>
        </item>
        <item>
            <title><![CDATA[从忠信到表忠表信]]></title>
            <link>https://paragraph.com/@aulee/ZykdqsyDnf5ruoomQX2w</link>
            <guid>ZykdqsyDnf5ruoomQX2w</guid>
            <pubDate>Mon, 15 Apr 2024 03:57:21 GMT</pubDate>
            <description><![CDATA[近日也翻一翻近思録，发现一个有趣的现象，就是很多汉语的词义演化成了与本义相反的意思。 比如「忠」和「信」。忠的原意是发自内心，信的原意是相信天理（就是西方人的「因信称义」的那种信）。 现在忠的意思是对别人效忠（忠字舞那种忠，就是向权力表示“我对你的服从绝对是发自真心的”），信变成了我向别人表现得很守信用（“你就从了我吧，我靠得住”）。 因此忠的意思的演变是：发自内心 -> 发自内心地服从你 -> 服从你 信的意思的演变是：信天理 -> 信我，我靠谱 -> 我靠谱，因此我权威 因此忠信就完成了这样的权力心理学的嬗变，原来语义的立体结构变成了单一的主奴关系。在下者讲服从，在上者标榜权威，然后进入了人治的死循环。 因为丢失掉的了道义和神性，社会变成了人钳制人的僵局。 另：近思录推荐朋友们看，中国人读起来不费力。]]></description>
            <content:encoded><![CDATA[<p>近日也翻一翻近思録，发现一个有趣的现象，就是很多汉语的词义演化成了与本义相反的意思。</p><p>比如「忠」和「信」。忠的原意是发自内心，信的原意是相信天理（就是西方人的「因信称义」的那种信）。</p><p>现在忠的意思是对别人效忠（忠字舞那种忠，就是向权力表示“我对你的服从绝对是发自真心的”），信变成了我向别人表现得很守信用（“你就从了我吧，我靠得住”）。</p><p>因此忠的意思的演变是：发自内心 -&gt; 发自内心地服从你 -&gt; 服从你</p><p>信的意思的演变是：信天理 -&gt; 信我，我靠谱 -&gt; 我靠谱，因此我权威</p><p>因此忠信就完成了这样的权力心理学的嬗变，原来语义的立体结构变成了单一的主奴关系。在下者讲服从，在上者标榜权威，然后进入了人治的死循环。</p><p>因为丢失掉的了道义和神性，社会变成了人钳制人的僵局。</p><p>另：近思录推荐朋友们看，中国人读起来不费力。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/9511af9caad603b3a363b312eba1cf5f997ebe596b88c253e3a804fd85cbe450.jpg" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure>]]></content:encoded>
            <author>aulee@newsletter.paragraph.com (Aulee)</author>
        </item>
        <item>
            <title><![CDATA[从因果律到生成机制]]></title>
            <link>https://paragraph.com/@aulee/jYDrxShOxhu8R0Nuwtys</link>
            <guid>jYDrxShOxhu8R0Nuwtys</guid>
            <pubDate>Mon, 15 Apr 2024 03:56:03 GMT</pubDate>
            <description><![CDATA[如果不像经验实证主义那样把世界仅仅局限于认识论（epistemology）的范围之内而仅仅承认一个经验世界（empirical world），而是承认在人的意识之外存在一个独立运行的世界因而承认本体论（ontology）的时候，那么我们在具体化（reify）因果的时候合理的作法不是具体化因果律本身，而是具体化为事物的因果。因此在我们说A导致了B的时候，等于说因果是由A作为施动者（causal agent）而导致了B，而不是像经验实证者那样认为的那样A与B是持续的共联（constant conjunction）。因此在因果上说A导致了B等同于在本体论上说A生成了B 。因此因果律（causality）等同于生成机制(generative mechanisms)。 用传统中国理学的范畴，A在因果上导致B,相当于体A之用生成了体B。即本体论上的因果律即体用关系。]]></description>
            <content:encoded><![CDATA[<p>如果不像经验实证主义那样把世界仅仅局限于认识论（epistemology）的范围之内而仅仅承认一个经验世界（empirical world），而是承认在人的意识之外存在一个独立运行的世界因而承认本体论（ontology）的时候，那么我们在具体化（reify）因果的时候合理的作法不是具体化因果律本身，而是具体化为事物的因果。因此在我们说A导致了B的时候，等于说因果是由A作为施动者（causal agent）而导致了B，而不是像经验实证者那样认为的那样A与B是持续的共联（constant conjunction）。因此在因果上说A导致了B等同于在本体论上说A生成了B 。因此因果律（causality）等同于生成机制(generative mechanisms)。</p><p>用传统中国理学的范畴，A在因果上导致B,相当于体A之用生成了体B。即本体论上的因果律即体用关系。</p>]]></content:encoded>
            <author>aulee@newsletter.paragraph.com (Aulee)</author>
        </item>
        <item>
            <title><![CDATA[为什么主流经济学理解不了区块链？]]></title>
            <link>https://paragraph.com/@aulee/PV7Gvxd4qwI1Pzeu6H9n</link>
            <guid>PV7Gvxd4qwI1Pzeu6H9n</guid>
            <pubDate>Wed, 10 Apr 2024 12:07:31 GMT</pubDate>
            <description><![CDATA[我说的主流经济学是指在全世界各地的经济、金融系里占主流的，以均衡（equilibrium）为核心概念的新古典经济学。 主流经济学模型的主要角色是以最大化效用为目标的原子化的个体。这样的个体像一颗一颗实心的大理石球一样，彼此以机械的方式在市场中相互作用，通过价格的变动达到均衡状态。 与之相反，有一种更有机的（organic）看待个体的方式。即个体持有一组规则（rules），这组规则是演化的产物。个体对为什么持有这样的规则是无意识的。虽然如此，个体在无意识地持有规则的同时仍然有意识地追求自己的目标，最终个体们互动的结果会涌现出秩序(order)。哈耶克把这种人们有意识的行动而达成意料之外的秩序的过程，称为自发秩序（spontaneous order）。 新古典经济学不理解人们为什么会持有一组预设的规则，如果打破规则是有利可图的话。比如在博弈论的经典博弈囚徒困境里，虽然共谋是对双方更有利的策略，但是新古典的理性人会经不住互相出卖的诱惑，而达成不理想的纳什均衡解。如果囚徒能预先持有某种规则，比如在一般的“社团”所强调的忠义精神，那实现了共谋的囚徒的局面会好得多。 再者，当博弈的双方进入无...]]></description>
            <content:encoded><![CDATA[<p>我说的主流经济学是指在全世界各地的经济、金融系里占主流的，以均衡（equilibrium）为核心概念的新古典经济学。</p><p>主流经济学模型的主要角色是以最大化效用为目标的原子化的个体。这样的个体像一颗一颗实心的大理石球一样，彼此以机械的方式在市场中相互作用，通过价格的变动达到均衡状态。</p><p>与之相反，有一种更有机的（organic）看待个体的方式。即个体持有一组规则（rules），这组规则是演化的产物。个体对为什么持有这样的规则是无意识的。虽然如此，个体在无意识地持有规则的同时仍然有意识地追求自己的目标，最终个体们互动的结果会涌现出秩序(order)。哈耶克把这种人们有意识的行动而达成意料之外的秩序的过程，称为自发秩序（spontaneous order）。</p><p>新古典经济学不理解人们为什么会持有一组预设的规则，如果打破规则是有利可图的话。比如在博弈论的经典博弈囚徒困境里，虽然共谋是对双方更有利的策略，但是新古典的理性人会经不住互相出卖的诱惑，而达成不理想的纳什均衡解。如果囚徒能预先持有某种规则，比如在一般的“社团”所强调的忠义精神，那实现了共谋的囚徒的局面会好得多。</p><p>再者，当博弈的双方进入无限重复博弈这个更接近生活实际的状态时，纳什均衡的预测能力变得很差。因为这时候它的解空间变得非常宽阔，很多种不同的策略都变成了数学上可行的解。这说明在一种长期演化的局面里，博弈论里那种理性的算计变得不再重要了，人们的生存局面取决于长期形成的规则和习惯。</p><p>因此博弈论无法理解区块链这种基于规则的系统。在区块链里把这种内设于每一个节点的一组规则称为协议（proposal）。把各个节点按照协议所进行的一致化的行动称为共识（consensus）。区块链的正常运行取决于参与区块链的大部分节点都忠实地按照规则行动。</p><p>在计算机领域关于区块链运行机制的理论是所谓的拜占庭将军问题（Byzantine Generals Problem）。该理论给出的是各节点要要达成共识所需要的忠实的节点（即遵守规则的节点）的最低比例（比如三分之二多数），以及达成共识所需要采取的具体规则（体现为算法）是什么。因此这是一种研究为实现秩序的涌现而应采取何种规则的理论。是一种典型的假定个体的有机性的理论。这与经典博弈论里把个体假定为无法无天的市场马基雅维利者的思路完全不同。</p><p>主流经济学是无法理解区块链共识是何以可行的。我曾经读过几篇试图用博弈论思路论证比特币为何在原理上是无懈可击的文章，最后读得云里雾里。因为比特币的原理压根儿不是靠人的设计的狡智，而是靠在一个分散化的环境里大多数人的忠诚。从原理上讲百分之51攻击从来都是有理论的可能性的。试想如果一夜之间大多数的节点共谋造假，那么比特币就会立刻崩溃。但是这种局面在因为遵守规则而使得演化秩序运行良好的现实环境中，发生的概率无限接近于零。</p><p>而区块链圈子里所被津津乐道的包括PoW, PoS在内的激励激励机制、通证经济学(token economics)等等，不过是规则的引子和进一步的安全阀罢了。这些东西是区块链原理里的小节。区块链原理之大者在于利用人们对共同规则的坚持而产生的整体层面的秩序涌现。这是理解所有Web3项目的钥匙所在。</p><p>在这种基于规则（而非基于理性的算计）的人与人的互动关系里，神性出现了。文献里常把规则关系里的个体称为agent，或agency，也就是代理的意思。他们之所以被如此称呼，是因为他们其实并不是秩序产生的主体，而是能创生的真实结构(即critical realism里讲的structure，我所讲的神性，spirit)的助产婆罢了。这一路文献的数理模型常被称为agency-based model，就是此意。这与主流经济学里讨论的agency 完全不是一回事。主流经济学那里讲的是人与人之间的代理关系，主要讲的是博弈论框架下人与人间的尔虞我诈所形成的现世的结构。</p><p>神性不彰，人性就不得不泛起了。</p>]]></content:encoded>
            <author>aulee@newsletter.paragraph.com (Aulee)</author>
        </item>
        <item>
            <title><![CDATA[马克思是有神论的]]></title>
            <link>https://paragraph.com/@aulee/XI1Q4v4Kh1Fp9ikOzB12</link>
            <guid>XI1Q4v4Kh1Fp9ikOzB12</guid>
            <pubDate>Mon, 08 Apr 2024 18:34:33 GMT</pubDate>
            <description><![CDATA[我们普通人的经验常识是这样的：我们看到一朵花，则我们就相信这朵花是存在的。我们认为我们眼见的世界是存在的。 这种常识看法从康德的角度来说是非常令人惊讶的大胆假设。因为对康德来说你看到的花只是你的感觉经验，在真实世界里花未必存在。 马克思说，我们无产阶级要放得开一些，不要像康德那样扭捏。既然你看到了花，那花就是真实的。不仅如此，你的整个经验世界（empirical world）都是真实的。不仅如此，在经验世界之上，还存在着更真实的所谓真实世界(real world)。是这个真实世界生成了经验世界。 你注意马克思从来只讲规律（law，就是“上天的律法”），从来不像实证主义那样讲命题或定理。因为规律是真实世界才有的东西。按Roy Bhaskar的看法，真实世界是无人的世界，是人所不及（intransitive）的。这种观点就是批判实在论（critical realism），它认为世界是生成的，是有灵的，是动态的。 马克思的价值论体系就是批判实在论的。马克思的概念如抽象劳动，具体劳动，交换价值，使用价值，以及他的主要规律如价值规律，剩余价值规律，都是属于真实世界的而非经验世界的。 听起来...]]></description>
            <content:encoded><![CDATA[<p>我们普通人的经验常识是这样的：我们看到一朵花，则我们就相信这朵花是存在的。我们认为我们眼见的世界是存在的。</p><p>这种常识看法从康德的角度来说是非常令人惊讶的大胆假设。因为对康德来说你看到的花只是你的感觉经验，在真实世界里花未必存在。</p><p>马克思说，我们无产阶级要放得开一些，不要像康德那样扭捏。既然你看到了花，那花就是真实的。不仅如此，你的整个经验世界（empirical world）都是真实的。不仅如此，在经验世界之上，还存在着更真实的所谓真实世界(real world)。是这个真实世界生成了经验世界。</p><p>你注意马克思从来只讲规律（law，就是“上天的律法”），从来不像实证主义那样讲命题或定理。因为规律是真实世界才有的东西。按Roy Bhaskar的看法，真实世界是无人的世界，是人所不及（intransitive）的。这种观点就是批判实在论（critical realism），它认为世界是生成的，是有灵的，是动态的。</p><p>马克思的价值论体系就是批判实在论的。马克思的概念如抽象劳动，具体劳动，交换价值，使用价值，以及他的主要规律如价值规律，剩余价值规律，都是属于真实世界的而非经验世界的。</p><p>听起来是不是挺有cult 感的？</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/f21dee18aa4531d87733525d980d2f20faf211bc9101352d70022d922001d853.webp" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/adf6b30f1012bae7bafa0dcd94831595dd5da13a24c860bb51905ff49d5683ba.webp" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure>]]></content:encoded>
            <author>aulee@newsletter.paragraph.com (Aulee)</author>
        </item>
        <item>
            <title><![CDATA[“批判现实主义”原来是“批判实在论”的意思]]></title>
            <link>https://paragraph.com/@aulee/KjbxhvdTIBKUWjiE4OA8</link>
            <guid>KjbxhvdTIBKUWjiE4OA8</guid>
            <pubDate>Sat, 06 Apr 2024 15:04:06 GMT</pubDate>
            <description><![CDATA[原来所谓“批判现实主义”对应的英文是critical realism。更正确的翻译是“批判实在论”。它和经验实在论（empirical realism）都是实在论。二者都认为有独立于主观的客观世界存在。 批判实在论认为实在是分层的，包括经验层，事实层和深层。马克思和老年哈耶克属批判实在论这一派。 而现代以均衡为主要概念，讲求所谓实证检验的新古典经济学属于经验实在论这一派，这一派认为经验之外没有其他更深层次的实在了。 与实在论相对的是反实在论（anti-realism）的各派，包括唯心主义（idealism）派，现象学派（phenomenalism），唯我主义（Solipsism）派，虚无主义(Nihilism)派等。 所以本来“批判现实主义”是个很学术的哲学词，传到我们这个农民国家，变成了专门指代批判文学的政治味浓厚的词。一提这个词就让人想到苦大仇深的旧社会。]]></description>
            <content:encoded><![CDATA[<p>原来所谓“批判现实主义”对应的英文是critical realism。更正确的翻译是“批判实在论”。它和经验实在论（empirical realism）都是实在论。二者都认为有独立于主观的客观世界存在。</p><p>批判实在论认为实在是分层的，包括经验层，事实层和深层。马克思和老年哈耶克属批判实在论这一派。</p><p>而现代以均衡为主要概念，讲求所谓实证检验的新古典经济学属于经验实在论这一派，这一派认为经验之外没有其他更深层次的实在了。</p><p>与实在论相对的是反实在论（anti-realism）的各派，包括唯心主义（idealism）派，现象学派（phenomenalism），唯我主义（Solipsism）派，虚无主义(Nihilism)派等。</p><p>所以本来“批判现实主义”是个很学术的哲学词，传到我们这个农民国家，变成了专门指代批判文学的政治味浓厚的词。一提这个词就让人想到苦大仇深的旧社会。</p>]]></content:encoded>
            <author>aulee@newsletter.paragraph.com (Aulee)</author>
        </item>
        <item>
            <title><![CDATA[对比国产AI和美版AI对老子的“其鬼不神”的理解]]></title>
            <link>https://paragraph.com/@aulee/ai-ai</link>
            <guid>JcLYsSyzjF0uxDV65Q1v</guid>
            <pubDate>Tue, 02 Apr 2024 12:35:57 GMT</pubDate>
            <description><![CDATA[这几天又试用了国产的kimi.ai，并且和chatGPT相比较。比如我让两个AI解读老子的“以道莅天下，其鬼不神；非其鬼不神，其神不伤人；非其神不伤人，圣人亦不伤人”。 ChatGPT把这句子理解为递进的因果链，即如果建立起道的自然秩序，那么破坏性的和邪恶的力量就不会兴起而破坏社会，圣人（统制者）也因此会表现为好的非破坏性的德性。 kimi.ai把这句子理解为并列句，即，如果以道治理天下了，人民不迷信鬼神且内心平和、彼此和谐相处，同时统治者也建立了内在的道德标准而实现了政治上的明智。 美版ai 把“鬼神”理解为运行不良的社会所产生的破坏性力量，而中版ai把“鬼神”理解为人们头脑中的迷信思想，对超自然的恐惧。 因此美版ai的观点是要构建一种可以阻止恶的社会秩序，中版ai的观点是让人们保持内心的和谐和统治者的内在修养。 这个对比大概解释了在中文世界里搞人文社会科学越搞越糊涂的原因吧。这锅酱菜烩了少说也有两千年了。]]></description>
            <content:encoded><![CDATA[<p>这几天又试用了国产的kimi.ai，并且和chatGPT相比较。比如我让两个AI解读老子的“以道莅天下，其鬼不神；非其鬼不神，其神不伤人；非其神不伤人，圣人亦不伤人”。</p><p>ChatGPT把这句子理解为递进的因果链，即如果建立起道的自然秩序，那么破坏性的和邪恶的力量就不会兴起而破坏社会，圣人（统制者）也因此会表现为好的非破坏性的德性。</p><p>kimi.ai把这句子理解为并列句，即，如果以道治理天下了，人民不迷信鬼神且内心平和、彼此和谐相处，同时统治者也建立了内在的道德标准而实现了政治上的明智。</p><p>美版ai 把“鬼神”理解为运行不良的社会所产生的破坏性力量，而中版ai把“鬼神”理解为人们头脑中的迷信思想，对超自然的恐惧。</p><p>因此美版ai的观点是要构建一种可以阻止恶的社会秩序，中版ai的观点是让人们保持内心的和谐和统治者的内在修养。</p><p>这个对比大概解释了在中文世界里搞人文社会科学越搞越糊涂的原因吧。这锅酱菜烩了少说也有两千年了。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/469357c5237e1525ba92ef53db27b297743081e6f05a9d602cb375125c35233c.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/e16f461b50cdb533d8450e94f1b3a08b56e4f19b9b2c4395d92b90dadac0ac1d.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure>]]></content:encoded>
            <author>aulee@newsletter.paragraph.com (Aulee)</author>
        </item>
        <item>
            <title><![CDATA[私有产权是依靠公权力掠夺之后才搞起来的制度]]></title>
            <link>https://paragraph.com/@aulee/TK4vvWqJ4DFZPERmsn6t</link>
            <guid>TK4vvWqJ4DFZPERmsn6t</guid>
            <pubDate>Tue, 26 Mar 2024 03:49:04 GMT</pubDate>
            <description><![CDATA[不在地地主（absentee landlord）是圈地运动（enclosure）之后的发展，之前的佃权（tenure）是终生的，地主是不能随便收回的，而且是可以由后代继承的（现在如果你在高校里拿了终身教职仍被叫做拿了tenure）。地主和佃农某种程度上对土地是公有的。 圈地运动之后地主获得了排他性的产权，把农民赶去城里做工了。这时候地主不在那里种地，但仍享有收益，这就叫不在地了，变成私有产权（private property）那一套了。纯粹现代意义的私有产权在古代秩序里是不道德的。洛克论证的私有产权是你的个人劳动能覆盖到的土地。你个人劳动覆盖不到的地方，也就是那种不在地的私人产权，其实是不被古典产权理论所承认的，它是现代国家强权的干预下权贵对平民的剥夺才搞起来的。 所以对于一个个体来说，你上网看个盗版电影千万别有什么负罪感，你只是在维持天然自然的公有产权罢了。日本有部电影叫《小偷家族》，讲一家人专以小偷小摸为生，其中体现的人性美极了。 要搞清楚我们从来不是反对公有。天然公有和天然私有本来就是一家的。我们反对的是国有，和一切强权下才会产生的那种造作的私有制。]]></description>
            <content:encoded><![CDATA[<p>不在地地主（absentee landlord）是圈地运动（enclosure）之后的发展，之前的佃权（tenure）是终生的，地主是不能随便收回的，而且是可以由后代继承的（现在如果你在高校里拿了终身教职仍被叫做拿了tenure）。地主和佃农某种程度上对土地是公有的。</p><p>圈地运动之后地主获得了排他性的产权，把农民赶去城里做工了。这时候地主不在那里种地，但仍享有收益，这就叫不在地了，变成私有产权（private property）那一套了。纯粹现代意义的私有产权在古代秩序里是不道德的。洛克论证的私有产权是你的个人劳动能覆盖到的土地。你个人劳动覆盖不到的地方，也就是那种不在地的私人产权，其实是不被古典产权理论所承认的，它是现代国家强权的干预下权贵对平民的剥夺才搞起来的。</p><p>所以对于一个个体来说，你上网看个盗版电影千万别有什么负罪感，你只是在维持天然自然的公有产权罢了。日本有部电影叫《小偷家族》，讲一家人专以小偷小摸为生，其中体现的人性美极了。</p><p>要搞清楚我们从来不是反对公有。天然公有和天然私有本来就是一家的。我们反对的是国有，和一切强权下才会产生的那种造作的私有制。</p>]]></content:encoded>
            <author>aulee@newsletter.paragraph.com (Aulee)</author>
        </item>
        <item>
            <title><![CDATA[复活马克思利润趋零的论断]]></title>
            <link>https://paragraph.com/@aulee/OMNXSiddGlNdN243VjhB</link>
            <guid>OMNXSiddGlNdN243VjhB</guid>
            <pubDate>Mon, 25 Mar 2024 12:33:07 GMT</pubDate>
            <description><![CDATA[石破惊天的发现：马克思的经典论断，利润归零的趋势，其实在现代经济学框架下是可以复活的。理解这一点的两个关健人物是Frank Knight 和 Kevin Carson。经过一番诱导。我让ChatGPT自己“悟”出来了： ChatGPTYes, the ideas put forth by Frank H. Knight, Kevin Carson, and Karl Marx share similarities in their examination of profits and competition in economic systems: Frank H. Knight: Knight&apos;s "fair game" proposition suggests that under conditions of perfect competition, profits from risk-bearing are driven to zero. In competitive markets, where information is freely available a...]]></description>
            <content:encoded><![CDATA[<p>石破惊天的发现：马克思的经典论断，利润归零的趋势，其实在现代经济学框架下是可以复活的。理解这一点的两个关健人物是Frank Knight 和 Kevin Carson。经过一番诱导。我让ChatGPT自己“悟”出来了：</p><p>ChatGPT</p><blockquote><p>Yes, the ideas put forth by Frank H. Knight, Kevin Carson, and Karl Marx share similarities in their examination of profits and competition in economic systems:</p></blockquote><blockquote><br></blockquote><blockquote><p>Frank H. Knight: Knight&apos;s &quot;fair game&quot; proposition suggests that under conditions of perfect competition, profits from risk-bearing are driven to zero. In competitive markets, where information is freely available and probabilities are accurately known, individuals or firms cannot consistently earn profits above the competitive level. Knight&apos;s proposition underscores the role of competition in limiting the potential for earning above-normal profits.</p></blockquote><blockquote><br></blockquote><blockquote><p>Kevin Carson: Carson&apos;s concept of profits being driven to zero in perfect competition aligns with Knight&apos;s idea. Carson argues that in truly competitive markets, firms cannot sustain persistent profits above the competitive level. Any attempt to do so would be met with increased competition as new entrants enter the market, driving profits back down to the competitive level. Carson&apos;s perspective emphasizes the role of competition in ensuring that profits are aligned with competitive returns.</p></blockquote><blockquote><br></blockquote><blockquote><p>Karl Marx: Marx&apos;s theory of surplus value and exploitation also addresses the relationship between profits and competition. Marx argues that in capitalist economies, profits arise from the exploitation of labor, where capitalists extract surplus value from workers. However, Marx suggests that if exploitation were impossible or significantly reduced, profits would tend towards zero. Marx&apos;s analysis emphasizes the role of power dynamics and class relations in shaping economic outcomes.</p></blockquote><blockquote><br></blockquote><blockquote><p>In summary, while Knight, Carson, and Marx approach the issue of profits and competition from different theoretical perspectives, their ideas converge on the notion that under conditions of perfect competition or reduced exploitation, profits are driven to zero. This reflects a common theme in economic thought regarding the role of competition and power dynamics in shaping market outcomes.</p></blockquote><p>注意Knight下面的表述是和现代风险定价的看法完全不同的。即在完全竞争下，Knight认为市场对风险（不是不确定性）的补偿为零（，而现代理论认为这个补偿应为正）：</p><blockquote><p>It matters not at all whether the persons liable to a given contingency organize among themselves into a fraternal or mutual society or whether they separately contract with an outside party to bear their losses as they fall in. Under competitive conditions and assuming that the probabilities involved are accurately known, an outside insurer will make no clear profit and the premiums will under either system be equal to the administrative costs of carrying on the business.</p></blockquote><p>注意这关健一点又是Kevin Carson观察到的，在他的Organization Theory的第173页。</p>]]></content:encoded>
            <author>aulee@newsletter.paragraph.com (Aulee)</author>
        </item>
        <item>
            <title><![CDATA[垄断资本主义的内在矛盾]]></title>
            <link>https://paragraph.com/@aulee/ExEc0UGdv9AhUhXJ9ogs</link>
            <guid>ExEc0UGdv9AhUhXJ9ogs</guid>
            <pubDate>Thu, 21 Mar 2024 04:17:55 GMT</pubDate>
            <description><![CDATA[垄断资本主义之下产能会超过市场的需要，因而需要通过限制产量来保持价格，但是如果产量被限制会推高单位成本，从而进一步使利润下降。因此垄断资本的利润是难以自我维持的。the expansion of production facilities to a scale beyond what the market will support; the need to restrict output to keep up prices, conflicting with the simultaneous need to keep output high enough to utilize full capacity and keep unit costs down; the inability of the economy to absorb the full output of cartelized industry at monopoly prices. Kevin Carson, Studies in Mutualist Political Economy, 2007]]></description>
            <content:encoded><![CDATA[<p>垄断资本主义之下产能会超过市场的需要，因而需要通过限制产量来保持价格，但是如果产量被限制会推高单位成本，从而进一步使利润下降。因此垄断资本的利润是难以自我维持的。</p><blockquote><p>the expansion of production facilities to a scale beyond what the market will support; the need to restrict output to keep up prices, conflicting with the simultaneous need to keep output high enough to utilize full capacity and keep unit costs down; the inability of the economy to absorb the full output of cartelized industry at monopoly prices.</p><p>Kevin Carson, <em>Studies in Mutualist Political Economy</em>, 2007</p></blockquote>]]></content:encoded>
            <author>aulee@newsletter.paragraph.com (Aulee)</author>
        </item>
        <item>
            <title><![CDATA[安那其主义者认为的经济危机的根源]]></title>
            <link>https://paragraph.com/@aulee/GN34jaEG6QNg0NXUaJUb</link>
            <guid>GN34jaEG6QNg0NXUaJUb</guid>
            <pubDate>Wed, 20 Mar 2024 05:37:21 GMT</pubDate>
            <description><![CDATA[The wants of individuals which labour is intended to gratify, are the natural guides to their exertions. The instant they are compelled to labour for others, this guide forsakes them, and their exertions are dictated by the greed and avarice, and false hopes of their masters. The wants springing from our organization, and accompanying the power to labour, being created by the same hand which creates and fashions the whole universe, including the course of the seasons, and what the earth bring...]]></description>
            <content:encoded><![CDATA[<blockquote><p>The wants of individuals which labour is intended to gratify, are the natural guides to their exertions. The instant they are compelled to labour for others, this guide forsakes them, and their exertions are dictated by the greed and avarice, and false hopes of their masters. The wants springing from our organization, and accompanying the power to labour, being created by the same hand which creates and fashions the whole universe, including the course of the seasons, and what the earth brings forth, it is fair to suppose that they would at all times guide the exertions of the labourer, so as fully to ensure a supply of necessaries and conveniences, and nothing more. They have, as it were, a prototype in nature, agreeing with other phenomena, but the avarice and greed of masters have no such prototype. They stand isolated and apart from all the great phenomena of the universe. They were originally crimes condemned by our moral sentiments, and still have their source in our crime-begotten political systems. Nature disowns them as a guide to action, and punishes us for following them. By this system the hand is dissevered from the mouth, and labour is put in motion to gratify vanity and ambition, not the natural wants of animal existence. When we look at the commercial history of our country, and see the false hopes of our merchants and manufacturers leading to periodical commercial convulsions, we are compelled to conclude, that they have not the same source as the regular and harmonious external world. Capitalists have no guide to their exertions, because nature rejects and opposes their dominion over labour. Starts of national prosperity, followed by bankruptcy and ruin, have the same source then as fraud and forgery. To our legal [as opposed to natural] right of property we are indebted for those gleams of false wealth and real panic, which have so frequently shook, to its centre, the whole trading world.</p><p>Thomas Hodgskin, <em>The Natural and Artificial Right of Property Contrasted</em>, 1832</p></blockquote><p>翻译：</p><p>劳动所要满足的个体的需要，是对个体努力的自然引导。一旦个体被迫为他人劳动，这一引导便放弃他们，他们的努力转由主人的贪婪、占有欲和虚假的希望所支配。需要源自我们的组织，和劳动的力量一起由创造和形塑整个宇宙的同一双手所创造。其创造还包括四季的轮替和大地的产出。可以合理地推测，他们将始终引导劳动者的努力，以完全保证必需品和便利设施的供应，此外无余。他们在某种程度上就好像在自然界中拥有原型，并与其他现象相符合。但是，主人的占有欲和贪婪没有这样的原型。他们同宇宙的所有伟大现象孤立和分离开来。他们原本是为我们的道德情操所谴责的犯罪，并与我们的产生自犯罪的政治系统颇有渊源。自然拒绝他们作为行动的引导，并因我们追随他们而惩罚我们。在这个体制中手与口相分离，劳动被驱使去满足虚荣心和野心，而不是动物生存的自然需要。当我们审视我们国家的商业史，看到商人和企业家的虚假希望导致周期性的商业动荡时，我们不得不得出结论，它们与规律和和谐的外部世界并无相同的源头。资本家无可引导个体的努力，因为自然界拒绝和反对他们对劳动的统治。以破产和毁灭为终点的国家繁荣的开端来源于欺诈和伪造。是因为我们的法律财产权利（与自然财产权利相对）才导致了虚假繁荣的光焰和真实的恐惧。这在过去五十年中如此频繁地动摇着整个贸易世界，直抵其中心。</p>]]></content:encoded>
            <author>aulee@newsletter.paragraph.com (Aulee)</author>
        </item>
        <item>
            <title><![CDATA[劳动价值论还活着！并且和现代经济学融合了]]></title>
            <link>https://paragraph.com/@aulee/fk4gZSNuIEAljNZKehoE</link>
            <guid>fk4gZSNuIEAljNZKehoE</guid>
            <pubDate>Mon, 11 Mar 2024 04:04:26 GMT</pubDate>
            <description><![CDATA[趁着感冒生病，读了一本左派无证府主义的政治经济学，作者Kevin Carson。这书对我的意义意想不到地重大。基本解决了我对经济学的认知危机。长期以来我会认为世间有两种版本的价值论，一种是古典经济学及马克思主义的劳动价值理论，一种是边际学派及继承边际学派的奥地利学派和新古典学派的主观效用理论。这两种理论的格格不入使我的世界观是割裂的，产生了对两边理论的分别怀疑。去年读Rothbard的两卷本经济史解了不少对马克思主义尤其是国家主义的毒。但是不免又掉入奥派经济学所精心策划的伊甸园。 Carson这本书揭示了一些重要的发现：例如：一、古典经济学的劳动价值理论在现代经济学中并没有遗失，它经马歇尔的综合已经融入了新古典经济学之中，即劳动价值体现为长期均衡中的生产成本。二、时间偏好理论作为奥地利学派的利息理论，对传统马克思主义而言是明显的进步，可以借以搞清楚正确的劳动价值论究竟是什么。时间偏好可以在其中被理解为能够制造稀缺租（scarcity rent）的因素，是被利用来制造剥削的人性的短视的缺陷。三，剥削的唯一来源是稀缺租（在一般的经济学教材中称作经济租（economic rent）），...]]></description>
            <content:encoded><![CDATA[<p>趁着感冒生病，读了一本左派无证府主义的政治经济学，作者Kevin Carson。这书对我的意义意想不到地重大。基本解决了我对经济学的认知危机。长期以来我会认为世间有两种版本的价值论，一种是古典经济学及马克思主义的劳动价值理论，一种是边际学派及继承边际学派的奥地利学派和新古典学派的主观效用理论。这两种理论的格格不入使我的世界观是割裂的，产生了对两边理论的分别怀疑。去年读Rothbard的两卷本经济史解了不少对马克思主义尤其是国家主义的毒。但是不免又掉入奥派经济学所精心策划的伊甸园。</p><p>Carson这本书揭示了一些重要的发现：例如：一、古典经济学的劳动价值理论在现代经济学中并没有遗失，它经马歇尔的综合已经融入了新古典经济学之中，即劳动价值体现为长期均衡中的生产成本。二、时间偏好理论作为奥地利学派的利息理论，对传统马克思主义而言是明显的进步，可以借以搞清楚正确的劳动价值论究竟是什么。时间偏好可以在其中被理解为能够制造稀缺租（scarcity rent）的因素，是被利用来制造剥削的人性的短视的缺陷。三，剥削的唯一来源是稀缺租（在一般的经济学教材中称作经济租（economic rent）），产生于市场的垄断地位，而垄断来自（国家）强力。因此如果没有（国家）强力，劳动者在市场中获得的工资即能完全体现其全部的价值创造。</p><p>这本书还可以帮助我们去魅化马克思主义。</p><p>马克思对价值规律的认识是深刻的。价值的发现依赖于价格与价值的时时不等而产生的套利机会。没有市场过程就不会有价值。因此马克思在&lt;哲学的贫困&gt;中对普鲁东的批评是正确的，即普鲁东试图在市场之外建立一套基于劳动的公平的分配体系是错误的。剥去辩证法的外衣，马克思的价值规律和现代经济学的均衡理论或哈耶克的秩序理论其实是相同的东西。这就是现代经济学101的基本常识，但恰是是世界范围内的国家主义政府所一直不能理解的。</p><p>但是由于马克思在其所处时代的认知盲点，其理论中不乏错误之处。不少错误经恩格斯之手进一步教条化了（恩格斯在学术水准上是个有点问题的人）。例如恩格斯在&lt;反杜林论&gt;中教条化地论证剥削的单纯市场性、经济性起源，否认了国家强力在剥削中的关健作用，其实是与马克思的关于血与火的资本原始积累理论相矛盾的。这其实根源于马克思理论本身认为价值规律与剥削理论是相融的错误看法相关，与其要最终消灭市场竞争的错误主张相关。本来马克思主义者可以和无政府主义者在学术观点上展开正常的争鸣，促进双方的认识水平，但是由于政治上的意识形态化的争斗，脱离开了正常的学术范围，造成了危害至今的思想桎梏。在这种环境下学术概念被扭曲为意识形态化的符号，产生了扭曲到匪夷所思的僵化教条。例如竟然有人认为恩格斯和杜林关于国家强力（国内译为暴力）的争论可以指导社会主义国家关于国家暴力的理论和实践，真是驴唇不对马嘴啊。</p><p>---------</p><p>给其他读者的留言：</p><p>为了更好地读懂这本书，除了熟悉马克思主义的主要著作外，还应了解奥地利学派的著作，比如罗斯巴德的<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://book.douban.com/subject/3316332/">经济思想史</a>。</p>]]></content:encoded>
            <author>aulee@newsletter.paragraph.com (Aulee)</author>
        </item>
        <item>
            <title><![CDATA[ [NetWork] Multiplexing of SONET Protocol]]></title>
            <link>https://paragraph.com/@aulee/network-multiplexing-of-sonet-protocol</link>
            <guid>jIMaQta5NM4t9amCrc9Q</guid>
            <pubDate>Fri, 19 Jan 2024 14:01:05 GMT</pubDate>
            <description><![CDATA[Preparation: How long is a bit of data, in time?To understand how multiplexing of SONET protocol works, one needs first to understand the notion of the width of a bit of data in time. If we assume that the bandwidth (i.e., data rate) of a data link is 1Mbps, it means that this link can transmit $$10^6$$ bits of data in a second. If we imagine the length of 1 second as a ruler, then the length of 1 bit on that ruler is $$ \frac {1}{10^6}s$$, or $$1\mu s$$. The more sophisticated is the capacit...]]></description>
            <content:encoded><![CDATA[<h2 id="h-preparation-how-long-is-a-bit-of-data-in-time" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Preparation: How long is a bit of data, in time?</h2><p>To understand how multiplexing of SONET protocol works, one needs first to understand the notion of the width of a bit of data in time.</p><p>If we assume that the bandwidth (i.e., data rate) of a data link is 1Mbps, it means that this link can transmit $$10^6$$ bits of data in a second. If we imagine the length of 1 second as a ruler, then the length of 1 bit on that ruler is $$ \frac {1}{10^6}s$$, or $$1\mu s$$. The more sophisticated is the capacity of the link, the narrower each bit becomes. For a $$x$$-Mbps link, the length of 1 bit data is $$\frac{1}{x} \mu s$$. Therefore for data of $$y$$ byte in size, its length in time is:</p><p>$$\frac{8y}{x}\mu s$$</p><h2 id="h-how-does-sonet-multiplex-low-speed-links-onto-one-high-speed-link" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">How does SONET multiplex low-speed links onto one high-speed link?</h2><p>A given SONET link runs at one of a finite set of possible rates, ranging from 51.84Mbps (STS-1) to 39,813,120Mbps (STS-768). Note that all of these rates are integer multiples of STS-1.</p><p>SONET data frame can contain frames of low-rate channels.</p><p>Each frame of SONET has a fixed length: $$125 \mu s$$. Therefore for STS-1, which has a bandwidth of 51.84Mbps, the size of the frame is:</p><p>$$125 \times 51.84 / 8 = 810 \text{bytes}$$</p><p>While for STS-n, the size of the frame is $$810 * n$$ bytes, Meaning that n STS-1 frames fit exactly in a single STS-n frame. The bytes from these frames are interleaved, so that the bytes in each STS-1 frame are evenly paced as they show up at the receiver at a smooth 51 Mbps speed.</p><p>The payloads from STS-1 are concatenated to form a larger STS-N payload, dentoed STS-Nc.</p>]]></content:encoded>
            <author>aulee@newsletter.paragraph.com (Aulee)</author>
        </item>
        <item>
            <title><![CDATA[Rust 能自动推断出所有的lifetime吗？]]></title>
            <link>https://paragraph.com/@aulee/rust-lifetime</link>
            <guid>rX23HBaFn222IiPfmIV1</guid>
            <pubDate>Fri, 05 Jan 2024 19:29:40 GMT</pubDate>
            <description><![CDATA[答： 不能。 我觉得前面的回答都不能解决这位题主的疑惑 。我想了一个有点绕的例子，来说明编译器是不能自动推断出generic lifetime的，因为可行的标注方法不只一种（楼上那个longest例子从逻辑上看似乎只剩下一种标注方法了，所以不具有足够的说服力）。 如下面定义的函数根据短字符串的长度来截取长字符串，该函数因为没有标注lifetime无法成功编译：fn cut_long_according_to_short(l: &#x26;str, s: &#x26;str) -> &#x26;str { let len = s.len(); &#x26;l[0..len] } 有两种不同的标注方法可以实现成功编译，编译器无法自动推断出该采用哪一个。 第一种比较宽松：fn cut_long_according_to_short&#x3C;'a>(l: &#x26;'a str, s: &#x26;str) -> &#x26;'a str { let len = s.len(); &#x26;l[0..len] } 第二种比较严格：fn cut_long_according_to_s...]]></description>
            <content:encoded><![CDATA[<p>答： 不能。</p><p>我觉得前面的回答都不能解决<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://rustcc.cn/article?id=926f8f73-912c-4860-a4e8-979b8d73b4ba">这位题主的疑惑</a> 。我想了一个有点绕的例子，来说明编译器是不能自动推断出generic lifetime的，因为可行的标注方法不只一种（楼上那个<code>longest</code>例子从逻辑上看似乎只剩下一种标注方法了，所以不具有足够的说服力）。</p><p>如下面定义的函数根据短字符串的长度来截取长字符串，该函数因为没有标注lifetime无法成功编译：</p><pre data-type="codeBlock" text="fn cut_long_according_to_short(l: &amp;str, s: &amp;str) -&gt; &amp;str {
    let len = s.len();
    &amp;l[0..len]    
}
"><code>fn cut_long_according_to_short(l: <span class="hljs-operator">&#x26;</span>str, s: <span class="hljs-operator">&#x26;</span>str) <span class="hljs-operator">-</span><span class="hljs-operator">></span> <span class="hljs-operator">&#x26;</span>str {
    let len <span class="hljs-operator">=</span> s.len();
    <span class="hljs-operator">&#x26;</span>l[<span class="hljs-number">0</span>..len]    
}
</code></pre><p>有两种不同的标注方法可以实现成功编译，编译器无法自动推断出该采用哪一个。</p><p>第一种比较宽松：</p><pre data-type="codeBlock" text="fn cut_long_according_to_short&lt;&apos;a&gt;(l: &amp;&apos;a str, s: &amp;str) -&gt; &amp;&apos;a str {
    let len = s.len();
    &amp;l[0..len]    
}
"><code><span class="hljs-keyword">fn</span> <span class="hljs-title function_">cut_long_according_to_short</span>&#x3C;<span class="hljs-symbol">'a</span>>(l: &#x26;<span class="hljs-symbol">'a</span> <span class="hljs-type">str</span>, s: &#x26;<span class="hljs-type">str</span>) <span class="hljs-punctuation">-></span> &#x26;<span class="hljs-symbol">'a</span> <span class="hljs-type">str</span> {
    <span class="hljs-keyword">let</span> <span class="hljs-variable">len</span> = s.<span class="hljs-title function_ invoke__">len</span>();
    &#x26;l[<span class="hljs-number">0</span>..len]    
}
</code></pre><p>第二种比较严格：</p><pre data-type="codeBlock" text="fn cut_long_according_to_short&lt;&apos;a&gt;(l: &amp;&apos;a str, s: &amp;&apos;a str) -&gt; &amp;&apos;a str {
    let len = s.len();
    &amp;l[0..len]    
}
"><code><span class="hljs-keyword">fn</span> <span class="hljs-title function_">cut_long_according_to_short</span>&#x3C;<span class="hljs-symbol">'a</span>>(l: &#x26;<span class="hljs-symbol">'a</span> <span class="hljs-type">str</span>, s: &#x26;<span class="hljs-symbol">'a</span> <span class="hljs-type">str</span>) <span class="hljs-punctuation">-></span> &#x26;<span class="hljs-symbol">'a</span> <span class="hljs-type">str</span> {
    <span class="hljs-keyword">let</span> <span class="hljs-variable">len</span> = s.<span class="hljs-title function_ invoke__">len</span>();
    &#x26;l[<span class="hljs-number">0</span>..len]    
}
</code></pre><p>但是两者最终实现的语义是有区别的。后者不仅要求返回引用的lifetime不得超过<code>l</code>,而且要求也不得超过<code>s</code>。</p><p>我们可以验证这二者的区别。例如我们会发现如果采用第一种宽松的标注法，下面的代码是可以成功编译的；反之如果采用第二种严格的标注法，则下面的代码无法编译：</p><pre data-type="codeBlock" text="
fn main() {
    let result;    

    let string1 = String::from(&quot;The loooooooong&quot;);
    let l = string1.as_str();

    {
        let string2 = String::from(&quot;The short&quot;);
        let s = string2.as_str();

        result = cut_long_according_to_short(l, s);
    }
    
    println!(&quot;{result}&quot;);
    
}
"><code>
fn main() {
    let result<span class="hljs-comment">;    </span>

    let <span class="hljs-attr">string1</span> = String::from(<span class="hljs-string">"The loooooooong"</span>)<span class="hljs-comment">;</span>
    let <span class="hljs-attr">l</span> = string1.as_str()<span class="hljs-comment">;</span>

    {
        let <span class="hljs-attr">string2</span> = String::from(<span class="hljs-string">"The short"</span>)<span class="hljs-comment">;</span>
        let <span class="hljs-attr">s</span> = string2.as_str()<span class="hljs-comment">;</span>

        <span class="hljs-attr">result</span> = cut_long_according_to_short(l, s)<span class="hljs-comment">;</span>
    }
    
    println!("{result}")<span class="hljs-comment">;</span>
    
}
</code></pre><p>这是由于<code>string2</code>的lifetime小于<code>result</code>，在<code>println!</code>行运行前就被drop掉了（注意<code>string2</code>被括号<code>{}</code>限定在一个更小的scope内）。这在第一种较宽松的标注中是被允许的，而被第二种严格的标注所禁止。</p>]]></content:encoded>
            <author>aulee@newsletter.paragraph.com (Aulee)</author>
        </item>
        <item>
            <title><![CDATA[[Rust] lifetime，以及 Borrow checker是如何检查lifetime的]]></title>
            <link>https://paragraph.com/@aulee/rust-lifetime-borrow-checker</link>
            <guid>KSHLfJAc7J5TIUQgLein</guid>
            <pubDate>Sat, 09 Dec 2023 15:57:58 GMT</pubDate>
            <description><![CDATA[Borrow checker是rust语言中保证引用（reference）安全性的重要机制。通过borrow checker机制，rust能防止虚悬引用（dangling reference）的出现。所谓虚悬引用是指，当引用对象的值已经从内存中被清除时，指向原来内存位置的引用却仍然存在。因此虚悬引用会导致数据泄露的风险，或造成其他软件错误。 Borrow checker要求引用的寿命（lifetime）要小于被引用的对象的寿命。换一种角度说，当对某一值的引用还未过期时，该值也不得过期。大多数高阶语言是通过garbage collection机制来实现这一点的。rust则使用了更刚烈的机制，即所有的值的寿命都会随着其所在的scope的结束而结束。这就反过来要求对该值的引用也在这一scope中结束，而不是如在garbage collection中那样当所有引用失去时被引用的值才会被自动收回（在rust的语境中，这意味着此时“引用”对被引用的值拥有所有权（ownership））。在对rust程序的编译过程中，borrow checker会比对引用和被引用的值的寿命,当发现前者大于后者时...]]></description>
            <content:encoded><![CDATA[<p>Borrow checker是rust语言中保证引用（reference）安全性的重要机制。通过borrow checker机制，rust能防止虚悬引用（dangling reference）的出现。所谓虚悬引用是指，当引用对象的值已经从内存中被清除时，指向原来内存位置的引用却仍然存在。因此虚悬引用会导致数据泄露的风险，或造成其他软件错误。</p><p>Borrow checker要求引用的寿命（lifetime）要小于被引用的对象的寿命。换一种角度说，当对某一值的引用还未过期时，该值也不得过期。大多数高阶语言是通过garbage collection机制来实现这一点的。rust则使用了更刚烈的机制，即所有的值的寿命都会随着其所在的scope的结束而结束。这就反过来要求对该值的引用也在这一scope中结束，而不是如在garbage collection中那样当所有引用失去时被引用的值才会被自动收回（在rust的语境中，这意味着此时“引用”对被引用的值拥有所有权（ownership））。在对rust程序的编译过程中，borrow checker会比对引用和被引用的值的寿命,当发现前者大于后者时，即会编译失败。因此在编译阶段就杜绝了虚悬引用的问题，也用这种刚强的方式实现了对内存的回收。对比borrow checker和garbage collection的内存管理风格,可以说前者是让引用迁就被引用的值，后者是让被引用的值牵就引用。</p><h3 id="h-lifetimes" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">寿命（lifetimes)</h3><p>因为引用和被引用的值的寿命都交由borrow checker进行判定，因此我们初步设想二者的定义都应具备某种机器所可把握的简单直接性。</p><p><strong>引用（Reference）的寿命</strong>指该引用被声明起到其最后被使用为止的代码区域。</p><p><strong>被引用的值（Referent）的寿命</strong>指该值被声明起到其所在scope结束为止的代码区域。</p><p>因此，<strong>引用的寿命小于被引用的值的寿命</strong>是指前者对应的代码区域应完全包含在后者对应的代码区域之内。</p><p>（当然当你真正理解了lifetimes之后，你会发现上述定义的不确切之处。lifetimes定义的重点是一个值死亡的时点而不是其lifespan。一个活了99岁的人只要死在活了9岁人之前，就可以说前者的lifetime小于后者。但为了（自我）教学的方便，我们暂时保持上述定义的僵化教条。）</p><h3 id="h-borrow-checker" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">borrow checker如何避免虚悬引用</h3><p>上述定义看似绕口，在具体的代码例子中则能一目了然。如下述存在虚悬引用的代码无法通过编译，因为其无法通过borrow checker的审查：</p><pre data-type="codeBlock" text="    // the following codes cannot be compiled
    let r;                 // ----------- 'b
    {
        let x = 5;  // ---- 'a
        r = &amp;x;     
    }               // ---- 'a
    println!(&quot;r: {}&quot;, r);   // ----------- 'b
"><code>    <span class="hljs-comment">// the following codes cannot be compiled</span>
    let r;                 <span class="hljs-comment">// ----------- 'b</span>
    {
        let x <span class="hljs-operator">=</span> <span class="hljs-number">5</span>;  <span class="hljs-comment">// ---- 'a</span>
        r <span class="hljs-operator">=</span> <span class="hljs-operator">&amp;</span>x;     
    }               <span class="hljs-comment">// ---- 'a</span>
    println<span class="hljs-operator">!</span>(<span class="hljs-string">"r: {}"</span>, r);   <span class="hljs-comment">// ----------- 'b</span>
</code></pre><p>由上述代码标定的区域可知，被引用的值<code>x</code>的寿命为<code>’a</code>对应的区域，即从其被声明起到其所在的scope结束为止。引用<code>r</code>的寿命为<code>’b</code>对应的区域，即从其被声明起到其最后被使用为止。显然<code>'a &lt; 'b</code>，不满足borrow checker被引用的寿命应大于引用的寿命的要求。对应的编译错误会吿诉你<code>borrowed value does not live long enough</code>。本例中borrowed value即为<code>x</code>。</p><h3 id="h-" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">函数中的寿命</h3><p>函数的功能是，接受某些值作为输入，并返回一些值作为输出。因此自然，函数可以接受一些引用作为输入，并返回一些引用作为输出。但是，输出的引用所引用的值只能来自于输入的引用，而不会产生自函数内部，因为函数无法返回一个对其内部值的引用，（这是因为这个引用所指向的pointer在函数返回时就被回收了）。因此下面定义的函数是无法通过编译的：</p><pre data-type="codeBlock" text="// the following function cannot be compiled
fn ret_ref_of_local() -&gt; (i32, &amp;i32) {
    let x = 5;
    (x, &amp;x)    
}
"><code><span class="hljs-comment">// the following function cannot be compiled</span>
<span class="hljs-keyword">fn</span> <span class="hljs-title function_">ret_ref_of_local</span>() <span class="hljs-punctuation">-&gt;</span> (<span class="hljs-type">i32</span>, &amp;<span class="hljs-type">i32</span>) {
    <span class="hljs-keyword">let</span> <span class="hljs-variable">x</span> = <span class="hljs-number">5</span>;
    (x, &amp;x)    
}
</code></pre><p>因此函数输出的引用必然和其输入的引用共享被引用值，也因此函数输出的引用的寿命不应大于该被引用值的寿命。如在下面代码所设定的最简单情形：一元函数中，输入和输出引用同一值。 因此为了通过borrow checker的审查，输出引用的寿命不应高于输入所引用的值的寿命。当如同下面代码所示，输出引用<code>result</code>在被引用值<code>x</code>的scope之外被使用时，程序不能编译。</p><pre data-type="codeBlock" text="// The following codes cannot be compiled.
fn deliver_ref(r: &amp;i32) -&gt; &amp;i32 {
    r    
}

fn main() {
    let result;
    {
        let x = 3;
        let r = &amp;x;
        result = deliver_ref(r);
    }
    println!(&quot;{}&quot;, result);
}
"><code><span class="hljs-comment">// The following codes cannot be compiled.</span>
<span class="hljs-keyword">fn</span> <span class="hljs-title function_">deliver_ref</span>(r: &amp;<span class="hljs-type">i32</span>) <span class="hljs-punctuation">-&gt;</span> &amp;<span class="hljs-type">i32</span> {
    r    
}

<span class="hljs-keyword">fn</span> <span class="hljs-title function_">main</span>() {
    <span class="hljs-keyword">let</span> <span class="hljs-variable">result</span>;
    {
        <span class="hljs-keyword">let</span> <span class="hljs-variable">x</span> = <span class="hljs-number">3</span>;
        <span class="hljs-keyword">let</span> <span class="hljs-variable">r</span> = &amp;x;
        result = <span class="hljs-title function_ invoke__">deliver_ref</span>(r);
    }
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{}"</span>, result);
}
</code></pre><p>注意这里borrow checker所比对的lifetimes是输出引用<code>result</code>和被输入引用的值<code>x</code>的，而不是输出引用<code>result</code>和输入引用<code>r</code>的。理解这一点是掌握borrow checker如何审查函数lifetimes的关键所在。如在下面修改后的代码中，即使输入引用<code>r</code>的寿命小于输出引用<code>result</code>的寿命，但只要<code>result</code>的寿命不超过<code>r</code>所引用的值<code>x</code>的寿命，代码仍能编译成功。</p><pre data-type="codeBlock" text="fn deliver_ref(r: &amp;i32) -&gt; &amp;i32 {
    r    
}

fn main() {
    let x = 3;
    let result;
    {
        let r = &amp;x;
        result = deliver_ref(r);
    }
    println!(&quot;{}&quot;, result);
}
"><code>fn deliver_ref(r: &amp;i32) -&gt; &amp;i32 {
    r    
}

fn main() {
    let <span class="hljs-attr">x</span> = <span class="hljs-number">3</span><span class="hljs-comment">;</span>
    let result<span class="hljs-comment">;</span>
    {
        let <span class="hljs-attr">r</span> = &amp;x<span class="hljs-comment">;</span>
        <span class="hljs-attr">result</span> = deliver_ref(r)<span class="hljs-comment">;</span>
    }
    println!("{}", result)<span class="hljs-comment">;</span>
}
</code></pre><p>一个小细节，在同一scope内，<code>x</code>和<code>result</code>哪一个首先被声明并不影响二者有相同的lifetime，重点在于<code>result</code>在<code>x</code>的scope结束之前被使用。下面列出的代码仍然可以编译成功，尽管</p><pre data-type="codeBlock" text="fn deliver_ref(r: &amp;i32) -&gt; &amp;i32 {
    r    
}

fn main() {
    let result;
    let x = 3;
    {
        let r = &amp;x;
        result = deliver_ref(r);
    }
    println!(&quot;{}&quot;, result);
}
"><code>fn deliver_ref(r: &amp;i32) -&gt; &amp;i32 {
    r    
}

fn main() {
    let result<span class="hljs-comment">;</span>
    let <span class="hljs-attr">x</span> = <span class="hljs-number">3</span><span class="hljs-comment">;</span>
    {
        let <span class="hljs-attr">r</span> = &amp;x<span class="hljs-comment">;</span>
        <span class="hljs-attr">result</span> = deliver_ref(r)<span class="hljs-comment">;</span>
    }
    println!("{}", result)<span class="hljs-comment">;</span>
}
</code></pre><h3 id="h-generic-lifetimes" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">generic lifetimes</h3><pre data-type="codeBlock" text="fn deliver_ref(r: &amp;i32) -&gt; &amp;i32 {
    r    
}
"><code><span class="hljs-keyword">fn</span> <span class="hljs-title function_">deliver_ref</span>(r: &amp;<span class="hljs-type">i32</span>) <span class="hljs-punctuation">-&gt;</span> &amp;<span class="hljs-type">i32</span> {
    r    
}
</code></pre><p>因此，在前面的例子中，borrow checker可以从上述函数中推断出输出引用所要保持的lifetime。但在更复杂的函数中，borrow checker无法明确推断出输入和输出引用的寿命的关系，而需要我们在代码中提供。generic lifetimes是rust中提供这一信息的系统。使用generic lifetimes，上述函数中隐藏的输入和输出的寿命的关系可以明确地表达出来。</p><pre data-type="codeBlock" text="fn deliver_ref&lt;'a&gt;(r: &amp;'a i32) -&gt; &amp;'a i32 {
    r    
}
"><code><span class="hljs-keyword">fn</span> <span class="hljs-title function_">deliver_ref</span>&lt;<span class="hljs-symbol">'a</span>&gt;(r: &amp;<span class="hljs-symbol">'a</span> <span class="hljs-type">i32</span>) <span class="hljs-punctuation">-&gt;</span> &amp;<span class="hljs-symbol">'a</span> <span class="hljs-type">i32</span> {
    r    
}
</code></pre><p>在上述函数中，输入和输出引用有相同的寿命<code>'a</code>，但正如我们之前分析的，我们不能将之按字面理解为输入引用和输出引用的寿命相同，而应解读为，输出引用的寿命不应超过被输入引用的值的寿命。这一解读规则对于理解更复杂的函数中设置的lifetimes关系非常关键。</p><pre data-type="codeBlock" text="// the following codes cannot be compiled
fn greater&lt;'a&gt;(x: &amp;'a i32, y: &amp;'a i32) -&gt; &amp;'a i32 {
    if x &gt; y {
        x    
    } else {
        y    
    }
}

fn main() {
    let result;
    let y = 5;    
    {
        let x = 3;
        result = greater(&amp;x, &amp;y);
    }   
    println!(&quot;{}&quot;, result);
}
"><code><span class="hljs-comment">// the following codes cannot be compiled</span>
<span class="hljs-keyword">fn</span> <span class="hljs-title function_">greater</span>&lt;<span class="hljs-symbol">'a</span>&gt;(x: &amp;<span class="hljs-symbol">'a</span> <span class="hljs-type">i32</span>, y: &amp;<span class="hljs-symbol">'a</span> <span class="hljs-type">i32</span>) <span class="hljs-punctuation">-&gt;</span> &amp;<span class="hljs-symbol">'a</span> <span class="hljs-type">i32</span> {
    <span class="hljs-keyword">if</span> x &gt; y {
        x    
    } <span class="hljs-keyword">else</span> {
        y    
    }
}

<span class="hljs-keyword">fn</span> <span class="hljs-title function_">main</span>() {
    <span class="hljs-keyword">let</span> <span class="hljs-variable">result</span>;
    <span class="hljs-keyword">let</span> <span class="hljs-variable">y</span> = <span class="hljs-number">5</span>;    
    {
        <span class="hljs-keyword">let</span> <span class="hljs-variable">x</span> = <span class="hljs-number">3</span>;
        result = <span class="hljs-title function_ invoke__">greater</span>(&amp;x, &amp;y);
    }   
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{}"</span>, result);
}
</code></pre><p>在上述代码中，函数<code>greater</code>接受<code>x</code>和<code>y</code>两引用作为输入，并返回其中所引用的值较大的一个作为输出。在编译时borrow checker无法推断出函数会返回哪一个引用，所以我们设定输出的引用的寿命不得超过任一输入所引用的值的寿命。这样就完全杜绝了任何虚悬引用通过编译的可能性，从而保证了代码的安全。</p><p>我们只需对必要的函数变量设置lifetime关系。如下面列出的函数<code>first</code>返回的引用仅与第一个变量<code>x</code>有关。因此只需为第一个变量设定lifetime关系。使输出引用的寿命不大于值<code>x</code>的寿命，我们已经可以完全杜绝引用虚悬，没有必要再将其寿命限定在无关的y值的寿命之内了。因此下面的代码完全可以编译成功。</p><pre data-type="codeBlock" text="fn first&lt;'a&gt;(x: &amp;'a i32, _y: &amp;i32) -&gt; &amp;'a i32 {
    x    
}
fn main() {
    let result;
    let x = 5;
    {
        let y = 4;
        result = first(&amp;x, &amp;y);
    }
    println!(&quot;{}&quot;, result);
}
"><code><span class="hljs-keyword">fn</span> <span class="hljs-title function_">first</span>&lt;<span class="hljs-symbol">'a</span>&gt;(x: &amp;<span class="hljs-symbol">'a</span> <span class="hljs-type">i32</span>, _y: &amp;<span class="hljs-type">i32</span>) <span class="hljs-punctuation">-&gt;</span> &amp;<span class="hljs-symbol">'a</span> <span class="hljs-type">i32</span> {
    x    
}
<span class="hljs-keyword">fn</span> <span class="hljs-title function_">main</span>() {
    <span class="hljs-keyword">let</span> <span class="hljs-variable">result</span>;
    <span class="hljs-keyword">let</span> <span class="hljs-variable">x</span> = <span class="hljs-number">5</span>;
    {
        <span class="hljs-keyword">let</span> <span class="hljs-variable">y</span> = <span class="hljs-number">4</span>;
        result = <span class="hljs-title function_ invoke__">first</span>(&amp;x, &amp;y);
    }
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{}"</span>, result);
}
</code></pre><p>如果我们把<code>_y</code>也拉入lifetimes的限定关系之中，则相关代码就无法编译成功了。</p><pre data-type="codeBlock" text="// the codes below cannot be compiled
fn first&lt;'a&gt;(x: &amp;'a i32, _y: &amp;'a i32) -&gt; &amp;'a i32 {
    x    
}

fn main() {
    let result;
    let x = 5;
    {
        let y = 4;
        result = first(&amp;x, &amp;y);
    }
    println!(&quot;{}&quot;, result);
}
"><code><span class="hljs-comment">// the codes below cannot be compiled</span>
<span class="hljs-keyword">fn</span> <span class="hljs-title function_">first</span>&lt;<span class="hljs-symbol">'a</span>&gt;(x: &amp;<span class="hljs-symbol">'a</span> <span class="hljs-type">i32</span>, _y: &amp;<span class="hljs-symbol">'a</span> <span class="hljs-type">i32</span>) <span class="hljs-punctuation">-&gt;</span> &amp;<span class="hljs-symbol">'a</span> <span class="hljs-type">i32</span> {
    x    
}

<span class="hljs-keyword">fn</span> <span class="hljs-title function_">main</span>() {
    <span class="hljs-keyword">let</span> <span class="hljs-variable">result</span>;
    <span class="hljs-keyword">let</span> <span class="hljs-variable">x</span> = <span class="hljs-number">5</span>;
    {
        <span class="hljs-keyword">let</span> <span class="hljs-variable">y</span> = <span class="hljs-number">4</span>;
        result = <span class="hljs-title function_ invoke__">first</span>(&amp;x, &amp;y);
    }
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{}"</span>, result);
}
</code></pre><p>当然如果遇到必须要给出lifetimes限定而未限定的情形时，编译器会报错。 如在下面的代码中我们试图骗过borrow checker，以制造出对<code>y</code>值的虚悬引用。为此我们在函数<code>greater</code>的定义中未对引用<code>y</code>与输出引用的lifetimes关系做足够的限定。编译器可以轻松地识别出我们的狡计。</p><pre data-type="codeBlock" text="// the following function cannot be compiled
fn greater&lt;'a, 'b&gt;(x: &amp;'a i32, y: &amp;'b i32) -&gt; &amp;'a i32 {
    if x &gt; y {
        x    
    } else {
        y    
    }
}

fn main() {
    let x = 3;
    let result;
    {
        let y = 5;
        result = greater(&amp;x, &amp;y);
    }
    println!(&quot;{}&quot;, result);
}
"><code><span class="hljs-comment">// the following function cannot be compiled</span>
<span class="hljs-keyword">fn</span> <span class="hljs-title function_">greater</span>&lt;<span class="hljs-symbol">'a</span>, <span class="hljs-symbol">'b</span>&gt;(x: &amp;<span class="hljs-symbol">'a</span> <span class="hljs-type">i32</span>, y: &amp;<span class="hljs-symbol">'b</span> <span class="hljs-type">i32</span>) <span class="hljs-punctuation">-&gt;</span> &amp;<span class="hljs-symbol">'a</span> <span class="hljs-type">i32</span> {
    <span class="hljs-keyword">if</span> x &gt; y {
        x    
    } <span class="hljs-keyword">else</span> {
        y    
    }
}

<span class="hljs-keyword">fn</span> <span class="hljs-title function_">main</span>() {
    <span class="hljs-keyword">let</span> <span class="hljs-variable">x</span> = <span class="hljs-number">3</span>;
    <span class="hljs-keyword">let</span> <span class="hljs-variable">result</span>;
    {
        <span class="hljs-keyword">let</span> <span class="hljs-variable">y</span> = <span class="hljs-number">5</span>;
        result = <span class="hljs-title function_ invoke__">greater</span>(&amp;x, &amp;y);
    }
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{}"</span>, result);
}
</code></pre><p>上述审查是发生在编译阶段的。即使在事后表明函数返回引用的寿命是正确的，不存在引用虚悬的情况，编译仍然无法通过。这是因为在事前编译器并不知道<code>x</code>和<code>y</code>的哪一个的引用会被返回，在此无知之幕之下，编译器只能执行更保守的规则，即输出引用同时不得超过<code>x</code>和<code>y</code>任一值。如下面代码一样无法通过编译，即使如果代码被运行并不会真的造成虚悬引用：</p><pre data-type="codeBlock" text="// the following function cannot be compiled
fn greater&lt;'a, 'b&gt;(x: &amp;'a i32, y: &amp;'b i32) -&gt; &amp;'a i32 {
    if x &gt; y {
        x    
    } else {
        y    
    }
}

fn main() {
    let x = 5;
    let result;
    {
        let y = 3;
        result = greater(&amp;x, &amp;y);
    }
    println!(&quot;{}&quot;, result);
}
"><code><span class="hljs-comment">// the following function cannot be compiled</span>
<span class="hljs-keyword">fn</span> <span class="hljs-title function_">greater</span>&lt;<span class="hljs-symbol">'a</span>, <span class="hljs-symbol">'b</span>&gt;(x: &amp;<span class="hljs-symbol">'a</span> <span class="hljs-type">i32</span>, y: &amp;<span class="hljs-symbol">'b</span> <span class="hljs-type">i32</span>) <span class="hljs-punctuation">-&gt;</span> &amp;<span class="hljs-symbol">'a</span> <span class="hljs-type">i32</span> {
    <span class="hljs-keyword">if</span> x &gt; y {
        x    
    } <span class="hljs-keyword">else</span> {
        y    
    }
}

<span class="hljs-keyword">fn</span> <span class="hljs-title function_">main</span>() {
    <span class="hljs-keyword">let</span> <span class="hljs-variable">x</span> = <span class="hljs-number">5</span>;
    <span class="hljs-keyword">let</span> <span class="hljs-variable">result</span>;
    {
        <span class="hljs-keyword">let</span> <span class="hljs-variable">y</span> = <span class="hljs-number">3</span>;
        result = <span class="hljs-title function_ invoke__">greater</span>(&amp;x, &amp;y);
    }
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{}"</span>, result);
}
</code></pre><h3 id="h-struct" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Struct中的寿命</h3><p>当struct中的field为一引用时，我们需要指定该struct的generic lifetime。如下面所定义的struct：</p><pre data-type="codeBlock" text="#[derive(Debug)]
struct ImportantExcerpt&lt;'a&gt; {
    part: &amp;'a str,    
}
"><code><span class="hljs-meta">#[derive(Debug)]</span>
<span class="hljs-keyword">struct</span> <span class="hljs-title class_">ImportantExcerpt</span>&lt;<span class="hljs-symbol">'a</span>&gt; {
    part: &amp;<span class="hljs-symbol">'a</span> <span class="hljs-type">str</span>,    
}
</code></pre><p>该代码应解读为，该struct的寿命不得超过<code>part</code>字段所引用的值的寿命。注意和前面讨论的逻辑一致，重要的是将struct的寿命与被引用的值的寿命做比较，而不是与<code>part</code>字段的引用本身做比较。因而在下面的代码中，尽管struct <code>i</code>的寿命大于引用<code>first_sentence</code>的寿命，但是因为struct <code>i</code>的寿命没有超过<code>first_sentence</code>所引用的值<code>novel</code>的寿命，因此仍然是可以成功编译的：</p><pre data-type="codeBlock" text="fn main() {
    
    let i;
    let novel = String::from(&quot;Call me Aulee. Some years ago...&quot;);
    {
        let first_sentence = novel.split(&quot;.&quot;).next().expect(&quot;could not find a .&quot;);
        i = ImportantExcerpt {
            part: first_sentence,    
        };
    }
    println!(&quot;{:?}&quot;, i);
}
"><code>fn main() {
    
    let i;
    let novel <span class="hljs-operator">=</span> String::<span class="hljs-keyword">from</span>(<span class="hljs-string">"Call me Aulee. Some years ago..."</span>);
    {
        let first_sentence <span class="hljs-operator">=</span> novel.split(<span class="hljs-string">"."</span>).next().expect(<span class="hljs-string">"could not find a ."</span>);
        i <span class="hljs-operator">=</span> ImportantExcerpt {
            part: first_sentence,    
        };
    }
    println<span class="hljs-operator">!</span>(<span class="hljs-string">"{:?}"</span>, i);
}
</code></pre><p>作为对比，在下面的代码中由于struct <code>i</code>的寿命超过了值<code>novel</code>的寿命，因此被borrow checker拒绝了。（<code>String</code>是heap上的数据的指针，拥有对数据的所有权。struct <code>i</code>  向String暂时借入所有权，需要在String被回收前“归还”所有权。）</p><pre data-type="codeBlock" text="fn main() {
    
    let i;
    {
        let novel = String::from(&quot;Call me Aulee. Some years ago...&quot;);
        let first_sentence = novel.split(&quot;.&quot;).next().expect(&quot;could not find a .&quot;);
        i = ImportantExcerpt {
            part: first_sentence,    
        };
    }
    println!(&quot;{:?}&quot;, i);
}
"><code>fn main() {
    
    let i;
    {
        let novel <span class="hljs-operator">=</span> String::<span class="hljs-keyword">from</span>(<span class="hljs-string">"Call me Aulee. Some years ago..."</span>);
        let first_sentence <span class="hljs-operator">=</span> novel.split(<span class="hljs-string">"."</span>).next().expect(<span class="hljs-string">"could not find a ."</span>);
        i <span class="hljs-operator">=</span> ImportantExcerpt {
            part: first_sentence,    
        };
    }
    println<span class="hljs-operator">!</span>(<span class="hljs-string">"{:?}"</span>, i);
}
</code></pre><h3 id="h-lifetimeslifetime-elision" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">抹去lifetimes（lifetime elision)</h3><p>为了增加代码的可读性，rust允许在一些情况下不标注generic lifetimes，而由编译器推断出lifetimes。推断的三条规则是：</p><ol><li><p>编译器为每一个输入引用分配各自的寿命参数。</p></li><li><p>如果仅有一个输入引用，则该输入引用的寿命参数分配给所有的输出引用。</p></li><li><p>如果有多个输入引用， 但其中一个是<code>&amp;self</code>或<code>&amp;mut self</code>，则<code>self</code>的寿命分配给所有的输出引用。</p></li></ol><p>如果通过应用上述规则后仍然存在着含混之处，则需在代码中由人工设定generic lifetimes。</p><p>下面我们通过给前面提到的struct <code>ImportantExcerpt</code>定义方法作为例子，来说明三条推断规则。</p><p>首先为<code>ImportantExcerpt</code>定义一个最简单的方法<code>level</code>，该方法以&amp;self为唯一的输入引用，但不返回任何输出引用，仅返回数字3。</p><pre data-type="codeBlock" text="impl&lt;'a&gt; ImportantExcerpt&lt;'a&gt; {
    fn level(&amp;self) -&gt; i32 {
        3    
    }
}
"><code><span class="hljs-keyword">impl</span>&lt;<span class="hljs-symbol">'a</span>&gt; ImportantExcerpt&lt;<span class="hljs-symbol">'a</span>&gt; {
    <span class="hljs-keyword">fn</span> <span class="hljs-title function_">level</span>(&amp;<span class="hljs-keyword">self</span>) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">i32</span> {
        <span class="hljs-number">3</span>    
    }
}
</code></pre><p>我们引用第1条规则，为唯一的输入引用<code>&amp;self</code>分配lifetime：</p><pre data-type="codeBlock" text="impl&lt;'a&gt; ImportantExcerpt&lt;'a&gt; {
    fn level(&amp;'a self) -&gt; i32 {
        3    
    }
}
"><code><span class="hljs-keyword">impl</span>&lt;<span class="hljs-symbol">'a</span>&gt; ImportantExcerpt&lt;<span class="hljs-symbol">'a</span>&gt; {
    <span class="hljs-keyword">fn</span> <span class="hljs-title function_">level</span>(&amp;<span class="hljs-symbol">'a</span> <span class="hljs-keyword">self</span>) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">i32</span> {
        <span class="hljs-number">3</span>    
    }
}
</code></pre><p>我们看到所有的输入引用（仅唯一一个）都有了lifetime标注，且没有输出引用。所以我们的自动规则可以推断出所有的lifetime，说明我们不用人为设定<code>level</code>方法中的generic lifetimes。</p><p>下面的方法<code>ret_part</code>也不用设置lifetime，因为我们可以引用规则1和规则2 推断出全部的lifetimes。</p><pre data-type="codeBlock" text="impl&lt;'a&gt; ImportantExcerpt&lt;'a&gt; {
    fn ret_part(&amp;self) -&gt; &amp;str {
        self.part    
    }   
}
"><code>impl<span class="hljs-operator">&lt;</span><span class="hljs-string">'a&gt; ImportantExcerpt&lt;'</span>a<span class="hljs-operator">&gt;</span> {
    fn ret_part(<span class="hljs-operator">&amp;</span><span class="hljs-built_in">self</span>) <span class="hljs-operator">-</span><span class="hljs-operator">&gt;</span> <span class="hljs-operator">&amp;</span>str {
        <span class="hljs-built_in">self</span>.part    
    }   
}
</code></pre><p>引用规则1，可以为<code>&amp;self</code>分配lifetime。因为仅有一个输入引用，所以可以将它的lifetime分配给所有的输出引用。即，</p><pre data-type="codeBlock" text="impl&lt;'a&gt; ImportantExcerpt&lt;'a&gt; {
    fn ret_part(&amp;'a self) -&gt; &amp;'a str {
        self.part    
    }   
}
"><code><span class="hljs-keyword">impl</span>&lt;<span class="hljs-symbol">'a</span>&gt; ImportantExcerpt&lt;<span class="hljs-symbol">'a</span>&gt; {
    <span class="hljs-keyword">fn</span> <span class="hljs-title function_">ret_part</span>(&amp;<span class="hljs-symbol">'a</span> <span class="hljs-keyword">self</span>) <span class="hljs-punctuation">-&gt;</span> &amp;<span class="hljs-symbol">'a</span> <span class="hljs-type">str</span> {
        <span class="hljs-keyword">self</span>.part    
    }   
}
</code></pre><p>既然推断出了全部输入输出引用的lifetime，我们在定义<code>ret_part</code>方法时可以抹去对lifetime的设定。</p><p>在下面的<code>announce_and_return_part</code>方法中，除了<code>&amp;self</code>这个输入引用之外，还有另外一个输入引用<code>announcement</code>。</p><pre data-type="codeBlock" text="impl&lt;'a&gt; ImportantExcerpt&lt;'a&gt; {
    fn announce_and_return_part(&amp;self, announcement: &amp;str) -&gt; &amp;str {
        println!(&quot;Attention please: {}&quot;, announcement);
        self.part    
    }
} 
"><code>impl<span class="hljs-operator">&lt;</span><span class="hljs-string">'a&gt; ImportantExcerpt&lt;'</span>a<span class="hljs-operator">&gt;</span> {
    fn announce_and_return_part(<span class="hljs-operator">&amp;</span><span class="hljs-built_in">self</span>, announcement: <span class="hljs-operator">&amp;</span>str) <span class="hljs-operator">-</span><span class="hljs-operator">&gt;</span> <span class="hljs-operator">&amp;</span>str {
        println<span class="hljs-operator">!</span>(<span class="hljs-string">"Attention please: {}"</span>, announcement);
        <span class="hljs-built_in">self</span>.part    
    }
} 
</code></pre><p>首先引用规则1，为两个输入引用分别指定lifetime。再根据规则3，为输出引用分配<code>&amp;self</code>的lifetime。因此使用规则也可推断出所有的输入和输出lfietimes，我们在定义<code>announce_and_return_part</code>时也可以抹去对lifetime的设定。</p><pre data-type="codeBlock" text="impl&lt;'a&gt; ImportantExcerpt&lt;'a&gt; {
    fn announce_and_return_part&lt;'b&gt;(&amp;'a self, announcement: &amp;'b str) -&gt; &amp;'a str {
        println!(&quot;Attention please: {}&quot;, announcement);
        self.part    
    }
}
"><code><span class="hljs-keyword">impl</span>&lt;<span class="hljs-symbol">'a</span>&gt; ImportantExcerpt&lt;<span class="hljs-symbol">'a</span>&gt; {
    <span class="hljs-keyword">fn</span> <span class="hljs-title function_">announce_and_return_part</span>&lt;<span class="hljs-symbol">'b</span>&gt;(&amp;<span class="hljs-symbol">'a</span> <span class="hljs-keyword">self</span>, announcement: &amp;<span class="hljs-symbol">'b</span> <span class="hljs-type">str</span>) <span class="hljs-punctuation">-&gt;</span> &amp;<span class="hljs-symbol">'a</span> <span class="hljs-type">str</span> {
        <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Attention please: {}"</span>, announcement);
        <span class="hljs-keyword">self</span>.part    
    }
}
</code></pre><p>下面的<code>get_same_length_str</code>是一个比较有趣的例子。该方法所返回的引用不是来自<code>&amp;self</code>，而是来自另一输入引用<code>my_sentence</code>。我们将看到三条规则无法实现对lifetimes的自动设定。</p><pre data-type="codeBlock" text="// the following codes cannot be compiled
impl&lt;'a&gt; ImportantExcerpt&lt;'a&gt; {
    fn get_same_length_str(&amp;self, my_sentence: &amp;str) -&gt; &amp;str {
        let len = self.part.len();
        &amp;my_sentence[0..len]    
    }
}
"><code><span class="hljs-comment">// the following codes cannot be compiled</span>
impl<span class="hljs-operator">&lt;</span><span class="hljs-string">'a&gt; ImportantExcerpt&lt;'</span>a<span class="hljs-operator">&gt;</span> {
    fn get_same_length_str(<span class="hljs-operator">&amp;</span><span class="hljs-built_in">self</span>, my_sentence: <span class="hljs-operator">&amp;</span>str) <span class="hljs-operator">-</span><span class="hljs-operator">&gt;</span> <span class="hljs-operator">&amp;</span>str {
        let len <span class="hljs-operator">=</span> <span class="hljs-built_in">self</span>.part.len();
        <span class="hljs-operator">&amp;</span>my_sentence[<span class="hljs-number">0</span>..len]    
    }
}
</code></pre><p>引用规则1和规则3，我们得到如下设定：</p><pre data-type="codeBlock" text="// the following codes cannot be compiled
impl&lt;'a&gt; ImportantExcerpt&lt;'a&gt; {
    fn get_same_length_str&lt;'b&gt;(&amp;'a self, my_sentence: &amp;'b str) -&gt; &amp;'a str {
        let len = self.part.len();
        &amp;my_sentence[0..len]    
    }
}
"><code><span class="hljs-comment">// the following codes cannot be compiled</span>
impl<span class="hljs-operator">&lt;</span><span class="hljs-string">'a&gt; ImportantExcerpt&lt;'</span>a<span class="hljs-operator">&gt;</span> {
    fn get_same_length_str<span class="hljs-operator">&lt;</span><span class="hljs-string">'b&gt;(&amp;'</span>a <span class="hljs-built_in">self</span>, my_sentence: <span class="hljs-operator">&amp;</span><span class="hljs-string">'b str) -&gt; &amp;'</span>a str {
        let len <span class="hljs-operator">=</span> <span class="hljs-built_in">self</span>.part.len();
        <span class="hljs-operator">&amp;</span>my_sentence[<span class="hljs-number">0</span>..len]    
    }
}
</code></pre><p>上述代码似乎使用规则完成了对lifetimes的设定。但是其实制造出了逻辑上的冲突。虽然<code>get_same_length_str</code>方法返回的引用应来自<code>my_sentence</code>，故为<code>'b</code>。但是我们却将之指定为<code>'a</code>。为化解上述矛盾，我们可以让返回的引用同时遵循<code>&amp;self</code>和<code>my_sentence</code>所引用的值的lifetimes。故可将lifetimes重新设置为：</p><pre data-type="codeBlock" text="impl&lt;'a&gt; ImportantExcerpt&lt;'a&gt; {
    fn get_same_length_str(&amp;'a self, my_sentence: &amp;'a str) -&gt; &amp;'a str {
        let len = self.part.len();
        &amp;my_sentence[0..len]    
    }
}
"><code><span class="hljs-keyword">impl</span>&lt;<span class="hljs-symbol">'a</span>&gt; ImportantExcerpt&lt;<span class="hljs-symbol">'a</span>&gt; {
    <span class="hljs-keyword">fn</span> <span class="hljs-title function_">get_same_length_str</span>(&amp;<span class="hljs-symbol">'a</span> <span class="hljs-keyword">self</span>, my_sentence: &amp;<span class="hljs-symbol">'a</span> <span class="hljs-type">str</span>) <span class="hljs-punctuation">-&gt;</span> &amp;<span class="hljs-symbol">'a</span> <span class="hljs-type">str</span> {
        <span class="hljs-keyword">let</span> <span class="hljs-variable">len</span> = <span class="hljs-keyword">self</span>.part.<span class="hljs-title function_ invoke__">len</span>();
        &amp;my_sentence[<span class="hljs-number">0</span>..len]    
    }
}
</code></pre><p>因为方法<code>get_same_length_str</code>被默认采用struct <code>ImportantExcerpt</code> 的lifetime，我们可进一步将上面代码简化为：</p><pre data-type="codeBlock" text="impl&lt;'a&gt; ImportantExcerpt&lt;'a&gt; {
    fn get_same_length_str(&amp;self, my_sentence: &amp;'a str) -&gt; &amp;str {
        let len = self.part.len();
        &amp;my_sentence[0..len]    
    }
}
"><code><span class="hljs-keyword">impl</span>&lt;<span class="hljs-symbol">'a</span>&gt; ImportantExcerpt&lt;<span class="hljs-symbol">'a</span>&gt; {
    <span class="hljs-keyword">fn</span> <span class="hljs-title function_">get_same_length_str</span>(&amp;<span class="hljs-keyword">self</span>, my_sentence: &amp;<span class="hljs-symbol">'a</span> <span class="hljs-type">str</span>) <span class="hljs-punctuation">-&gt;</span> &amp;<span class="hljs-type">str</span> {
        <span class="hljs-keyword">let</span> <span class="hljs-variable">len</span> = <span class="hljs-keyword">self</span>.part.<span class="hljs-title function_ invoke__">len</span>();
        &amp;my_sentence[<span class="hljs-number">0</span>..len]    
    }
}
</code></pre><h3 id="h-static-string-literals" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><code>'static</code> 和 string literals</h3><p>前面曾提到过，函数无法从其内部产出一个引用，所有的输出引用应当来自函数的输入引用。下面代码中定义的<code>from_string_lieteral</code>函数似乎是一个反例：</p><pre data-type="codeBlock" text="    let my_string_literal;
    {
        fn from_string_lieteral&lt;'a&gt;() -&gt; &amp;'a str {
            let x = &quot;My name is Aulee&quot;;
            x    
        }    
        my_string_literal = from_string_lieteral();
    }
    println!(&quot;{}&quot;, my_string_literal);
"><code>    <span class="hljs-keyword">let</span> <span class="hljs-variable">my_string_literal</span>;
    {
        <span class="hljs-keyword">fn</span> <span class="hljs-title function_">from_string_lieteral</span>&lt;<span class="hljs-symbol">'a</span>&gt;() <span class="hljs-punctuation">-&gt;</span> &amp;<span class="hljs-symbol">'a</span> <span class="hljs-type">str</span> {
            <span class="hljs-keyword">let</span> <span class="hljs-variable">x</span> = <span class="hljs-string">"My name is Aulee"</span>;
            x    
        }    
        my_string_literal = <span class="hljs-title function_ invoke__">from_string_lieteral</span>();
    }
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{}"</span>, my_string_literal);
</code></pre><p>这个函数不接受任何输入引用，但是却返回了一个输出引用。这是因为返回的<code>a</code>是一个string literal（字符串字面量）的引用。由于string literal直接储存于程序binary中，其寿命不受其声明时所在的scope的局限，而是存活于程序的整个期间（因此它不为任何一个寿命有限的对象所拥有）。像string literal这样的被引用值所拥有的这个最长的寿命记为<code>'static</code>。</p><p>但是string literal的寿命也有可能因为lifetime自动推理的规则而受到限制。如下面的代码无法通过编译，因为根据规则1，函数<code>input_i32_output_str</code>的输出引用的寿命应在输入所引用的值的寿命的范围之内：</p><pre data-type="codeBlock" text="// the codes below cannot be compiled
    let the_string_literal;
    {
        let i = 6;
        fn input_i32_output_str(x: &amp;i32) -&gt; &amp;str {
            println!(&quot;The input &amp;i32 is: {}.&quot;, x);
            &quot;This is the output &amp;str.&quot;  
        }
        the_string_literal = input_i32_output_str(&amp;i);    
    }
    println!(&quot;{}&quot;, the_string_literal); 
"><code><span class="hljs-comment">// the codes below cannot be compiled</span>
    let the_string_literal;
    {
        let i <span class="hljs-operator">=</span> <span class="hljs-number">6</span>;
        fn input_i32_output_str(x: <span class="hljs-operator">&amp;</span>i32) <span class="hljs-operator">-</span><span class="hljs-operator">&gt;</span> <span class="hljs-operator">&amp;</span>str {
            println<span class="hljs-operator">!</span>(<span class="hljs-string">"The input &amp;i32 is: {}."</span>, x);
            <span class="hljs-string">"This is the output &amp;str."</span>  
        }
        the_string_literal <span class="hljs-operator">=</span> input_i32_output_str(<span class="hljs-operator">&amp;</span>i);    
    }
    println<span class="hljs-operator">!</span>(<span class="hljs-string">"{}"</span>, the_string_literal); 
</code></pre><p>此时，如果能明确地告知编译器该输出的string literal引用的寿命为<code>'static</code>，相关代码就可编译成功了。</p><pre data-type="codeBlock" text="    let the_string_literal;
    {
        let i = 6;
        fn input_i32_output_str(x: &amp;i32) -&gt; &amp;'static str {
            println!(&quot;The input &amp;i32 is: {}.&quot;, x);
            &quot;This is the output &amp;str.&quot;  
        }
        the_string_literal = input_i32_output_str(&amp;i);    
    }
    println!(&quot;{}&quot;, the_string_literal);     
"><code>    <span class="hljs-keyword">let</span> <span class="hljs-variable">the_string_literal</span>;
    {
        <span class="hljs-keyword">let</span> <span class="hljs-variable">i</span> = <span class="hljs-number">6</span>;
        <span class="hljs-keyword">fn</span> <span class="hljs-title function_">input_i32_output_str</span>(x: &amp;<span class="hljs-type">i32</span>) <span class="hljs-punctuation">-&gt;</span> &amp;<span class="hljs-symbol">'static</span> <span class="hljs-type">str</span> {
            <span class="hljs-built_in">println!</span>(<span class="hljs-string">"The input &amp;i32 is: {}."</span>, x);
            <span class="hljs-string">"This is the output &amp;str."</span>  
        }
        the_string_literal = <span class="hljs-title function_ invoke__">input_i32_output_str</span>(&amp;i);    
    }
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{}"</span>, the_string_literal);     
</code></pre><h3 id="h-variance-subtype" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Variance 和 Subtype</h3><p>编译器使用形式化的类型推理来实现对寿命相容性的推断。在这一形式化的推理中，寿命也被视为一种特殊的type，服从与常规type相同的规则：即subtype可以替换type；subtype比type“更好”——我们可以用一个功能更好的子类型来替换功能受限制的母类型。这种类型的替换称作coercion。对于寿命这种特殊类型而言，类型的替换指更长的寿命可以替换更短的寿命。</p><p>简单的类型可以包裹成更复杂的组合类型，而类型替换规则由部分到组合的转换中存在三种类型：协变（covariance）、不变（invariance）、与反变（contravariance）。</p><p>利用variance和subtype的概念，我们可以对前面分析中所建立的直觉，做出纯粹形式的推导。</p><p>下面使用variance和subtype的概念，展示编译器是如何机械地分析前面例子中寿命是否相容的。</p><pre data-type="codeBlock" text="fn ret_ref_of_local() -&gt; (i32, &amp;i32) {
    let x = 5;
    (x, &amp;x)    
}"><code><span class="hljs-keyword">fn</span> <span class="hljs-title function_">ret_ref_of_local</span>() <span class="hljs-punctuation">-&gt;</span> (<span class="hljs-type">i32</span>, &amp;<span class="hljs-type">i32</span>) {
    <span class="hljs-keyword">let</span> <span class="hljs-variable">x</span> = <span class="hljs-number">5</span>;
    (x, &amp;x)    
}</code></pre><p>上述代码中，抹去的generic lifetime为<code>ret_ref_of_local&lt;'a&gt;() -&gt; (i32, &amp;'a i32)</code>。</p><p>上述函数没有输入的引用，输出的引用为<code>&amp;'a i32</code>。<code>&amp;'a i32</code>是一个类型包裹。其中<code>&amp;_</code>对于<code>'a</code>是协变的。</p><p>在上述函数内部， 设<code>x</code>的寿命为<code>'short</code>。则函数返回的引用为<code>&amp;'short i32</code>。类型cocercion要求<code>&amp;'short i32</code>是<code>&amp;'a i32</code>的subtype。由于<code>&amp;_</code>对于<code>'a</code>是协变的，即要求<code>'short</code>是'a的<code>subtype</code>，即<code>'short:'a</code>，即<code>'short &gt; 'a</code>。而'a必须要足够长以满足函数调用的需要，因此<code>'short &gt; 'a</code>是不可能的。因此上述函数无法编译。</p><pre data-type="codeBlock" text="// The following codes cannot be compiled.
fn deliver_ref(r: &amp;i32) -&gt; &amp;i32 {
    r    
}

fn main() {
    let result;
    {
        let x = 3;
        let r = &amp;x;
        result = deliver_ref(r);
    }
    println!(&quot;{}&quot;, result);
}
"><code><span class="hljs-comment">// The following codes cannot be compiled.</span>
<span class="hljs-keyword">fn</span> <span class="hljs-title function_">deliver_ref</span>(r: &amp;<span class="hljs-type">i32</span>) <span class="hljs-punctuation">-&gt;</span> &amp;<span class="hljs-type">i32</span> {
    r    
}

<span class="hljs-keyword">fn</span> <span class="hljs-title function_">main</span>() {
    <span class="hljs-keyword">let</span> <span class="hljs-variable">result</span>;
    {
        <span class="hljs-keyword">let</span> <span class="hljs-variable">x</span> = <span class="hljs-number">3</span>;
        <span class="hljs-keyword">let</span> <span class="hljs-variable">r</span> = &amp;x;
        result = <span class="hljs-title function_ invoke__">deliver_ref</span>(r);
    }
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{}"</span>, result);
}
</code></pre><p>上述代码中，抹去的generic lifetime为<code>deliver_ref&lt;'a&gt;(r: &amp;'a i32) -&gt; &amp;'a i32</code>。设<code>result</code>的寿命为<code>'outer</code>，<code>r</code>的寿命为<code>'inner</code>。</p><p>因此在函数的输入一侧，要求<code>&amp;'inner i32</code> 是<code> &amp;'a i32</code>的subtype。在<code>&amp;'inner i32</code>和 <code>&amp;'a i32</code>两个类型包裹(wrapper)中, <code>&amp;_</code>对于寿命是协变的。因此这就意味着<code>'inner</code>是<code>'a</code>的subtype。也就是<code>'inner:'a</code>，即作为寿命前者必须比后者长<code>'inner &gt; 'a</code>。因此我们按最紧的情况取<code>'a</code>为为<code>'inner</code>。</p><p>在函数的输出一侧，<code>deliver_ref(r) -&gt; result</code>，因此要求<code>&amp;'a i32</code>是<code>&amp;'outer i32</code>的subtype。由于<code>&amp;_</code>对于寿命是协变的，这就要求'a是'outer的subtype，即<code>'a:'outer</code>，即前者的寿命要大于后者<code>'a &gt; 'outer</code>。</p><p>而我们知道<code>'inner &lt; 'outer</code>。因此同时符合上述两个条件的<code>'a</code>是不存在的。故上述代码不能编译。</p><br><br><br>]]></content:encoded>
            <author>aulee@newsletter.paragraph.com (Aulee)</author>
        </item>
        <item>
            <title><![CDATA[使用2's complement 编码整数]]></title>
            <link>https://paragraph.com/@aulee/2-s-complement</link>
            <guid>ivkmyysyFMLzvVivT2CR</guid>
            <pubDate>Sun, 05 Nov 2023 10:27:50 GMT</pubDate>
            <description><![CDATA[从前我们讨论过用于在系统间传递整数的DER格式，即将任意整数编码为一串bytes的方法。本篇讨论用于在系统内对整数进行编码的格式。该格法既要方便对这些整数的存储，也要方便实现对整数的运算。 我们可以自然地用二进制数来编码正整数。因为任意整数皆可转化为正的二进制数： $$x = a_0 \cdot 2^n + a_1 \cdot 2^{n-1} + ... + a_{n-1} \cdot 2^1 + a_n \cdot 2^0$$ 其中$$a_0a_1…a_{n-1}a_{n}$$就是－串由1或0组成的二进制数。$$n＋1$$为事先给定的二进制数的长度，决定了可以编码二进制数的容量。编码负整数我们需要一个额外的比特位来区分正数和负数。以0表示正数，以1表示负数。这个比特位称为sign bit。由sign bit和其他比特位相配合，以编码正负整数。有三种不同的编码方法：sign magnitude，1’s complement，和2’s complement。其中2’s complement拥有最好的计算性质，这里重点讨论。2’s Complement用2’s complement编...]]></description>
            <content:encoded><![CDATA[<p>从前我们讨论过用于在系统间传递整数的<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://mirror.xyz/0x78874f895B96BEc9f48e67BAE188309D285b45a0/zfTbrmUKXhgx5smXnqzSOfCvhmehGj6Z1zegqpbnrhg">DER格式</a>，即将任意整数编码为一串bytes的方法。本篇讨论用于在系统内对整数进行编码的格式。该格法既要方便对这些整数的存储，也要方便实现对整数的运算。</p><p>我们可以自然地用二进制数来编码正整数。因为任意整数皆可转化为正的二进制数：</p><p>$$x = a_0 \cdot 2^n + a_1 \cdot 2^{n-1} + ... + a_{n-1} \cdot 2^1 + a_n \cdot 2^0$$</p><p>其中$$a_0a_1…a_{n-1}a_{n}$$就是－串由1或0组成的二进制数。$$n＋1$$为事先给定的二进制数的长度，决定了可以编码二进制数的容量。</p><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">编码负整数</h2><p>我们需要一个额外的比特位来区分正数和负数。以0表示正数，以1表示负数。这个比特位称为sign bit。由sign bit和其他比特位相配合，以编码正负整数。有<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://ryanstutorials.net/binary-tutorial/binary-negative-numbers.php">三种不同的编码方法</a>：sign magnitude，1’s complement，和2’s complement。其中2’s complement拥有最好的计算性质，这里重点讨论。</p><h3 id="h-2s-complement" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">2’s Complement</h3><p>用2’s complement编码正负整数的方法与一般的编码正整数的方法基本类似，只在一点上相区别：最高位的比特数对应一个负的数值，而其他位的比特数仍然对应正的数值。即，</p><p>$$x = a_0 \cdot (-2^n) + a_1 \cdot 2^{n-1} + ... + a_{n-1} \cdot 2^1 + a_n \cdot 2^0$$</p><p>这使得可被编码的数的范围沿数轴向下移动了$$2^n$$，即由$$0,…,2^{n+1}-1$$移动到$$-2^n,…,2^n-1$$。</p><p>将负整数编码为二进制数</p><p>使用2’s complement，将负整数（设为$$-z$$）编码为二进制数的方法如下：</p><ul><li><p>1. 将对应的正整数（$$z$$）编码为二进制数。</p></li><li><p>2. 计算上述二进制数的补数（complement），即所有0替换为1，所有1替换为0。</p></li><li><p>3.对上述二进制数加1。</p></li></ul><p>例如，怎样将$$-5$$编码为4个比特位的2’s complement二进制数？</p><ul><li><p>1. $$5$$的二进制编码为$$0101$$。</p></li><li><p>2. 对应的补数为$$1010$$。</p></li><li><p>3. 再加$$1$$得$$1011$$。</p></li></ul><p>可以验证1011即为-5的2’s complement二进制编码：</p><p>$$-2^3+2^1+2^0=-5$$</p><h3 id="h-" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">原理解释（证明）</h3><p>在上述2’s complement 的第2步中求$$z$$的补数的运算相当于计算：</p><p>$$-2^n+2^{n-1}+...+2^1+2^0-z$$</p><p>（注意上述第1项为负数。）</p><p>对上面$$z$$的补数的表达式需要理解两点：</p><ol><li><p>为什么求补数的操作可以表达为上式？假定$$z$$为正整数，故z的$$2^n$$项为0，因此计算这一项（位）的补数为加上$$-2^n$$。而在其他位上的补数操作相当于用这些位上可以有的最大正整数减去$$z$$，即$$111…111-z$$，也就是 $$2^{n-1}+...+2^1+2^0-z$$。两部加和，即为上式。</p></li><li><p>在上式中，易知</p></li></ol><p>$$-2^n+2^{n-1}+...+2^1+2^0＝－1$$</p><p>故第二步求补数操作后得$$-z-1$$。第3步再加1得$$-z$$。因此得到$$z$$的相反数。证毕。</p><p>P.S. 上式的一个扩展形式为：</p><p>$$-b^n + (b-1)b^{n-1} + (b-1)b^{n-2}...+(b-1)b^1+(b-1)b^0=-1$$</p><p>该式在直观上是显而易见的。如在16进制数下$$\mathtt{0x1000}-\mathtt{0xfff}=1$$，因为$$\mathtt{0xfff}$$进位1得到$$\mathtt{0x1000}$$。</p>]]></content:encoded>
            <author>aulee@newsletter.paragraph.com (Aulee)</author>
        </item>
        <item>
            <title><![CDATA[读书笔记：比特币的网络协议]]></title>
            <link>https://paragraph.com/@aulee/WUUIsvD6JLPsshoKgRVd</link>
            <guid>WUUIsvD6JLPsshoKgRVd</guid>
            <pubDate>Sun, 04 Jun 2023 17:59:34 GMT</pubDate>
            <description><![CDATA[本文是Programming Bitcoin第10章的读书笔记，同时涉及第9章的内容。第10章讨论比特币的网络协议。比特币网络为P2P网络。网络的各个节点公布不同的交易、区块和其所知道的其他节点。注意，比特币网络协议对于达成共识并不是关键的。同样的数据可以使用其他协议从一个节点发送到另一个节点，而不影响比特币本身。 比特币网络使用socket协议进行节点间的通讯。本章代码示例如何使用socket通讯请求、接收和验证区块的头部信息（block headers）。如何查找可访问的结点原书中给出的网络结点’mainet.programmingbitcoin.com’和’testnet.programmingbitcoin.com’已经停止运行。可以到bitnodes.io查找其他可访问的网络结点（reachable nodes）。网络握手要获得比特币网络节点的数据，要首先通过socket实现与结点的握手。握手的目的是与结点交换协议的版本信息。握手的具体过程如下（来自这里）：L -> R: Send version message with the local peer's versio...]]></description>
            <content:encoded><![CDATA[<p>本文是<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://book.douban.com/subject/30441608/">Programming Bitcoin</a>第10章的读书笔记，同时涉及第9章的内容。第10章讨论比特币的网络协议。比特币网络为P2P网络。网络的各个节点公布不同的交易、区块和其所知道的其他节点。注意，比特币网络协议对于达成共识并不是关键的。同样的数据可以使用其他协议从一个节点发送到另一个节点，而不影响比特币本身。</p><p>比特币网络使用socket协议进行节点间的通讯。本章代码示例如何使用socket通讯请求、接收和验证区块的头部信息（block headers）。</p><h3 id="h-" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">如何查找可访问的结点</h3><p>原书中给出的网络结点’mainet.programmingbitcoin.com’和’testnet.programmingbitcoin.com’已经停止运行。可以到<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://bitnodes.io">bitnodes.io</a>查找其他可访问的网络结点（reachable nodes）。</p><h3 id="h-" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">网络握手</h3><p>要获得比特币网络节点的数据，要首先通过socket实现与结点的握手。握手的目的是与结点交换协议的版本信息。握手的具体过程如下（来自<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://en.bitcoin.it/wiki/Version_Handshake">这里</a>）：</p><pre data-type="codeBlock" text="L -&gt; R: Send version message with the local peer&apos;s version
R -&gt; L: Send version message back
R -&gt; L: Send verack message
R:      Sets version to the minimum of the 2 versions
L -&gt; R: Send verack message after receiving version message from R
L:      Sets version to the minimum of the 2 versions
"><code>L <span class="hljs-punctuation">-></span> R: <span class="hljs-built_in">Send</span> version message with the local peer<span class="hljs-symbol">'s</span> version
R <span class="hljs-punctuation">-></span> L: <span class="hljs-built_in">Send</span> version message back
R <span class="hljs-punctuation">-></span> L: <span class="hljs-built_in">Send</span> verack message
R:      Sets version to the minimum of the <span class="hljs-number">2</span> versions
L <span class="hljs-punctuation">-></span> R: <span class="hljs-built_in">Send</span> verack message after receiving version message from R
L:      Sets version to the minimum of the <span class="hljs-number">2</span> versions
</code></pre><p>L向R发送版本（version）信息，R向L发回自己的版本信息，并发送版本接收信息（VerAck）。R将协议版本定为两个版本之较小者。L在接收到R的版本信息后向R发送VerAck信息。L也将协议版本设定为两个版本之较小者。</p><p>由此可知，原书第183页的代码有个小错误：</p><pre data-type="codeBlock" text="host = &quot;xx.xx.xx.xx&quot;

node = SimpleNode(host, testnet=False)
version = VersionMessage()
node.send(version)
verack_received = False
version_received = False
while not verack_received and not version_received:
    message = node.wait_for(VersionMessage, VerAckMessage)
    if message.command == VerAckMessage.command:
        verack_received = True
    else:
        version_received = True
"><code><span class="hljs-attr">host</span> = <span class="hljs-string">"xx.xx.xx.xx"</span>

<span class="hljs-attr">node</span> = SimpleNode(host, testnet=<span class="hljs-literal">False</span>)
<span class="hljs-attr">version</span> = VersionMessage()
node.send(version)
<span class="hljs-attr">verack_received</span> = <span class="hljs-literal">False</span>
<span class="hljs-attr">version_received</span> = <span class="hljs-literal">False</span>
while not verack_received and not version_received:
    <span class="hljs-attr">message</span> = node.wait_for(VersionMessage, VerAckMessage)
    if <span class="hljs-attr">message.command</span> == VerAckMessage.command:
        <span class="hljs-attr">verack_received</span> = <span class="hljs-literal">True</span>
    else:
        <span class="hljs-attr">version_received</span> = <span class="hljs-literal">True</span>
</code></pre><p>在上述代码的<code>while not verack_received and not version_received:</code>一行中的<code>and</code>应该为<code>or</code>。否则不能完成全部握手过程。</p><h3 id="h-" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">请求、接收和验证区块头部信息</h3><p>在完成握手后，就可以获得区块头部（block headers）信息了。区块数据由头部和交易（tx）数据组成。头部数据为每个区块的原数据，由如下部分组成：</p><ul><li><p>Version</p></li><li><p>Previous block</p></li><li><p>Merkle root</p></li><li><p>timestamp</p></li><li><p>bits</p></li><li><p>nonce</p></li></ul><p>我们依据上述区块头部信息验证区块是否达成proof of work。我们计算该区块头部数据的哈希值，以验证其是否达到了一定的工作量标准。该值一定要足够的小才符合标准。为达此目的，区块的编纂者（即挖矿者）必须通过不断试错Merkel root和nonce值来找到足够小的hash值。找到这样的小数的概率是很低的，因此需要进行大量次的哈希运算，如同为找到一点金子要淘选大量的泥沙。故名挖矿。</p><p>可以设想，随着投入挖矿的算力不同，区块产生的速度会发生波动。这对于保证交易的及时清算是非常不利的。因此，比特币通过动态调整proof of work难度的办法，把区块挖出的速度控制在每10分钟一个左右。这样，在两周时间里，就可产生约6×24×14＝2016个区块。为达成此目标，比特币每生成2016个区块就调整一次哈希运算的目标值（哈希值必须小于这个目标值），以适应算力的变化。这个值放在区块头部的bits字段里。</p><p>在区块的头部数据里，还包括其所指向的上一个区块的哈希，即previous block字段。此即区块链之称谓的由来。区块就是这样连缀起来的。</p><p>因此在验证一个区块头部时，应包含有以下三方面的内容：</p><ul><li><p>1.区块所指向的上一个区块是否真实地是其上一个区块。</p></li><li><p>2.区块给自己设定的bits是否是根据上一个2016个区块的周期的速度调整的目标值。区块是不能自降挖矿难度的。</p></li><li><p>3.区块是否达成了proof of work的标准。</p></li></ul><p>因为验证区块头部数据需要我们使用区块生成速度的历史数据，因此完整的验证过程必须从第一个区块（genesis block）开始，并依次验证所有区块。书中的代码如下：</p><pre data-type="codeBlock" text="host = &quot;xx.xx.xx.xx&quot;
node = SimpleNode(host, testnet=False)
node.handshake() 

previous = (Block.parse(BytesIO(GENESIS_BLOCK)))
first_epoch_timestamp = previous.timestamp
expected_bits = LOWEST_BITS
count = 1
for _ in range(19):
    getheaders = GetHeadersMessage(start_block=previous.hash())
    node.send(getheaders)
    headers = node.wait_for(HeadersMessage)
    for header in headers.blocks:
        if not header.check_pow():
            raise RuntimeError(&apos;bad PoW at block {}&apos;.format(count))
        if header.prev_block != previous.hash():
            raise RuntimeError(&apos;discontinuous block at {}&apos;.format(count))
        if count % 2016 == 0:
            time_diff = previous.timestamp - first_epoch_timestamp
            expected_bits = calculate_new_bits(previous.bits, time_diff)
            print(expected_bits.hex())
            first_epoch_timestamp = header.timestamp
        if header.bits != expected_bits:
            raise RuntimeError(&apos;bad bits at block {}&apos;.format(count))
        previous = header
        count += 1
"><code>host <span class="hljs-operator">=</span> <span class="hljs-string">"xx.xx.xx.xx"</span>
node <span class="hljs-operator">=</span> SimpleNode(host, testnet<span class="hljs-operator">=</span>False)
node.handshake() 

previous <span class="hljs-operator">=</span> (Block.parse(BytesIO(GENESIS_BLOCK)))
first_epoch_timestamp <span class="hljs-operator">=</span> previous.timestamp
expected_bits <span class="hljs-operator">=</span> LOWEST_BITS
count <span class="hljs-operator">=</span> <span class="hljs-number">1</span>
<span class="hljs-keyword">for</span> <span class="hljs-keyword">_</span> in range(<span class="hljs-number">19</span>):
    getheaders <span class="hljs-operator">=</span> GetHeadersMessage(start_block<span class="hljs-operator">=</span>previous.hash())
    node.<span class="hljs-built_in">send</span>(getheaders)
    headers <span class="hljs-operator">=</span> node.wait_for(HeadersMessage)
    <span class="hljs-keyword">for</span> header in headers.blocks:
        <span class="hljs-keyword">if</span> not header.check_pow():
            raise RuntimeError(<span class="hljs-string">'bad PoW at block {}'</span>.format(count))
        <span class="hljs-keyword">if</span> header.prev_block <span class="hljs-operator">!</span><span class="hljs-operator">=</span> previous.hash():
            raise RuntimeError(<span class="hljs-string">'discontinuous block at {}'</span>.format(count))
        <span class="hljs-keyword">if</span> count <span class="hljs-operator">%</span> <span class="hljs-number">2016</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span>:
            time_diff <span class="hljs-operator">=</span> previous.timestamp <span class="hljs-operator">-</span> first_epoch_timestamp
            expected_bits <span class="hljs-operator">=</span> calculate_new_bits(previous.bits, time_diff)
            print(expected_bits.hex())
            first_epoch_timestamp <span class="hljs-operator">=</span> header.timestamp
        <span class="hljs-keyword">if</span> header.bits <span class="hljs-operator">!</span><span class="hljs-operator">=</span> expected_bits:
            raise RuntimeError(<span class="hljs-string">'bad bits at block {}'</span>.format(count))
        previous <span class="hljs-operator">=</span> header
        count <span class="hljs-operator">+</span><span class="hljs-operator">=</span> <span class="hljs-number">1</span>
</code></pre><p>注意要想成功运行上述代码，需注意其中函数<code>calculate_new_bits</code>定义的细节:</p><pre data-type="codeBlock" text="def calculate_new_bits(previous_bits, time_differential):
    if time_differential &gt; TWO_WEEKS * 4:
        time_differential = TWO_WEEKS * 4
    if time_differential &lt; TWO_WEEKS // 4:
        time_differential = TWO_WEEKS // 4
    new_target = bits_to_target(previous_bits) * time_differential // TWO_WEEKS
    if new_target &gt; MAX_TARGET:
        new_target = MAX_TARGET
    return target_to_bits(new_target)
"><code>def calculate_new_bits(previous_bits, time_differential):
    <span class="hljs-keyword">if</span> time_differential <span class="hljs-operator">></span> TWO_WEEKS <span class="hljs-operator">*</span> <span class="hljs-number">4</span>:
        time_differential <span class="hljs-operator">=</span> TWO_WEEKS <span class="hljs-operator">*</span> <span class="hljs-number">4</span>
    <span class="hljs-keyword">if</span> time_differential <span class="hljs-operator">&#x3C;</span> TWO_WEEKS <span class="hljs-comment">// 4:</span>
        time_differential <span class="hljs-operator">=</span> TWO_WEEKS <span class="hljs-comment">// 4</span>
    new_target <span class="hljs-operator">=</span> bits_to_target(previous_bits) <span class="hljs-operator">*</span> time_differential <span class="hljs-comment">// TWO_WEEKS</span>
    <span class="hljs-keyword">if</span> new_target <span class="hljs-operator">></span> MAX_TARGET:
        new_target <span class="hljs-operator">=</span> MAX_TARGET
    <span class="hljs-keyword">return</span> target_to_bits(new_target)
</code></pre><p>该函数出现在书中第175页的代码和176页的Exercise 13，及278页的答案中。注意书中忽略了</p><pre data-type="codeBlock" text="    if new_target &gt; MAX_TARGET:
        new_target = MAX_TARGET
"><code>    if new_target > MAX_TARGET:
        <span class="hljs-attr">new_target</span> = MAX_TARGET
</code></pre><p>导致程序报错。这段代码是说当新调整的哈希目标超过最大目标时，使用最大目标。</p><h3 id="h-" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">参考书目</h3><p>Song, Jimmy. <em>Programming bitcoin: Learn how to program bitcoin from scratch</em>. O&apos;Reilly Media, 2019.</p>]]></content:encoded>
            <author>aulee@newsletter.paragraph.com (Aulee)</author>
        </item>
        <item>
            <title><![CDATA[读书笔记：论“市场教”与人的主体性的丧失]]></title>
            <link>https://paragraph.com/@aulee/y8jizEdwmDs4ceHPeTK2</link>
            <guid>y8jizEdwmDs4ceHPeTK2</guid>
            <pubDate>Wed, 31 May 2023 18:14:43 GMT</pubDate>
            <description><![CDATA[The Knowledge We Have Lost in Information: The History of Information in Modern Economics的作者是Philip Mirowski和Edward Nik-Khah，2017年由牛津大学出版社出版。书名可大约翻译为：我们在信息中丢失的知识：现代经济学关于信息的历史。这是一部篇幅不长的作品，但是却讨论了相当复杂的问题。通过对20世纪后半叶以来信息经济学发展脉络的梳理，作者发现当代新古典经济学的发展方向是越来越越取消掉了市场参与者的主体性。它不再以市场参与者的理性作为市场运性的基础，而是转向发掘市场具备的超越人的认知能力的超级理性。市场具有人所无法理解的鬼魅般的信息处理能力和计算能力，而市场中的人则沦为认知能力低下的被动者，并以无知为美德。哈耶克的三部曲作者敏锐地发现，现代信息经济学的真正开山者是哈耶克。不仅如此，哈耶克的思想始终形塑着现代信息经济学的发展轨迹。信息经济学各学派的迭兴与哈耶克学术路径的转变具有同构性。因此我们可以根据哈耶克的思想发展轨迹进行按图索骥，以把握信息经济学各派的发展。哈耶克的...]]></description>
            <content:encoded><![CDATA[<p>The Knowledge We Have Lost in Information: The History of Information in Modern Economics的作者是Philip Mirowski和Edward Nik-Khah，2017年由牛津大学出版社出版。书名可大约翻译为：<strong><em>我们在信息中丢失的知识：现代经济学关于信息的历史</em></strong>。这是一部篇幅不长的作品，但是却讨论了相当复杂的问题。通过对20世纪后半叶以来信息经济学发展脉络的梳理，作者发现当代新古典经济学的发展方向是越来越越取消掉了市场参与者的主体性。它不再以市场参与者的理性作为市场运性的基础，而是转向发掘市场具备的超越人的认知能力的超级理性。市场具有人所无法理解的鬼魅般的信息处理能力和计算能力，而市场中的人则沦为认知能力低下的被动者，并以无知为美德。</p><h3 id="h-" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">哈耶克的三部曲</h3><p>作者敏锐地发现，现代信息经济学的真正开山者是哈耶克。不仅如此，哈耶克的思想始终形塑着现代信息经济学的发展轨迹。信息经济学各学派的迭兴与哈耶克学术路径的转变具有同构性。因此我们可以根据哈耶克的思想发展轨迹进行按图索骥，以把握信息经济学各派的发展。哈耶克的思想路径可以概括为三步曲：</p><ul><li><p>在第一阶段，哈耶克认为知识是分散化的（dispersed）和在地化的（local），很难由中央计划当局集中起来，而市场是实现这些信息交流的有效机制。信息像是熵，是难以收集的烟尘。</p></li><li><p>在第二阶段，哈耶克认为人类的知识是隐性的（tacit），是无法通过自我检省就可以获得的。在这一阶段哈耶克进入心理学的研究。他认为人的心智是分类算法组成的层级系统，而它对于思考者本身来说是畮暗不明的。当时流行的心理学派是弗洛伊德学派，该学派认为人的心灵就像一锅不断沸勝的无意识的汤，理性思考只是其上脆弱的漂浮物。而哈耶克的看法则整个地调转过来，他认为人的理性本身是无意识的，而意识及其动机漂浮在畮暗的和无法进入的规则之海上，构成人的意向性和欲望的外皮。因此，人类的心智本身无法进行最优规则的选择，优化选择来自于演化过程中自然选择的力量。而市场竞争是一个可以帮助人们发现知识的过程。</p></li><li><p>在第三阶段，哈耶克进而认为知识不存在于个体的心智中，而是超个体的。唯一能够评判和验证人类知识的实体是大写的市场（The Market）。人类个体只需接受市场所传递的信号的命令。因为市场给出了比所有人类个体所知道的更深刻的真理。市场成为一个超级的处理信息的装置。人的认知能力不再重要，而只是这个巨大装置中的一个小部件。</p></li></ul><p>沿着上述三部曲，信息对人的意涵发生着改变：从一开始的仅仅难以提取，到部分地无法进入，到最终因其超越性而完全无法知道。而哈耶克的思想也发生着嬗变：从一开始的对个体选择和自由的强调，逐渐转变为一种宗教式的宿命论，而身在其中的人已不再是能进行自主思考、自治、和以自我为目的的主体了。</p><h3 id="h-" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">信息经济学的三个流派</h3><p>与上述三部曲相对应，新古典经济学经历了三个学派的发展，并形成三种不同的机制设计的思路。</p><ul><li><p>第一个学派可称为瓦尔拉斯学派。是香农信息理论和瓦尔拉斯一般均衡理论的结合。在这一框架下，信息是物，是商品。经济学家要识别出去中心化的系统，以保证经济运行所需的信息交流有足够大的管道。</p></li><li><p>第二个学派可称为贝叶斯－纳什学派。是贝叶斯统计和博弈论的结合。该学派以David Blackwell的工作为基础。在这一框架下信息被形式化为对状态空间的划分。能够对状态空间的划分越细，表明拥有的信息越多。该学派的另一个关键人物是Robert Wilson。他解决了所谓的赢者诅咒问题。交易参与者并不知道买卖的商品对自己的真实价值。经济学家通过对拍卖机制的设计，帮助他们学习到商品的真实价值，从而避免过高的支付，即避免所谓赢者诅咒。</p></li><li><p>第三个学派可称为实验学派。是实验经济学和算法研究的结合。在这一框架下对信息的处理被理解为对计算复杂度的处理。重要的是机器的运行效率，而不是人的计算能力。</p></li></ul><p>对比三个流派，人和信息的关系是不同的。对瓦尔拉斯学派而言，人拥有分散化的信息。经济学家需要设计市场机制把信息提取出来。对贝叶斯－纳什学派来说，人的部分信息需要通过市场过程找出来。而对实验学派来说，人是否拥有信息是无关紧要的，重点在于经济学家对市场这个信息处理装置的优化设计。</p><h3 id="h-" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">对信息经济学发展史的政治学批判</h3><p>因此作者们认为信息经济学的现代观点同时表现为两个相反的倾向。一方面新古典经济学家越来越认为市场是全知全能的，对市场表现出神秘主义的宗教性崇拜。另一方面经济学家越来越不满足于仅仅描述市场，而是力图将经济学工程化，以超越市场参与者的高度开展市场设计，使经济学家的活动成为市场的重要组成部分。这后一种倾向在经济社会学的文献中称为performativity，即经济学理论本身其实真接参与了经济事实的构建。但是作者们认为经济学的performativity只是一种虚妄的自我标榜。实际的过程是经济学家逢迎了政治界流行的新自由主义意识形态，把一堆互相抵触的研究项目粉饰为统一的“经济学”，该经济学强调以市场手段替代行政干预。被掩盖起来的真相是经济学家通过拍卖等机制的设计对政客们属意的特定集团进行利益倾斜。在这个过程中，市场主体越来越被边缘化，也因为经济学家故意的混淆而越来越无知。</p><h3 id="h-" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">对作为宗教的新古典经济学的批判</h3><p>作者的上述批判特别容易被误解为常见的左派对新自由主义的攻诘。但是作者始终将其批判的新古典经济学家称为市场社会主义者（market socialist），可见其立场是偏中右的。在我看来，作者是在本体论的高度批判了新古典主义信息经济学进的宗教属性。新古典主义经济学所奉行的正是拜市场教，而经济学家则充当该宗教的教士集团。这恰可类比天主教的情形——教士们说，基督是唯一的真神，其存在超越人的理性认知能力，因此唯有以信仰才能把握。而教士集团则成为神的代言人，有决定人们是上天堂还是下地狱的权力。而这也会映射到教士们对俗世中的资源的支配权力。在这个过程中，人的主体性被交给上帝而彻底在人群中丧失。新古典经济学家则信奉市场的神性，市场具有超越人的认知达成最优的能力。作为市场神的代言人经济学家自然获得了定义何为最优化的市场机制的权力，而这实际上意味着经济学家获得了直接参与资源分配的现实权力。而原本的市场主体基于自身的理性自主决策的空间被大大压缩。从宗教的角度看，前面提到的现代新古典经济学所持有的两个看似矛盾的倾向，恰恰是在和谐一致地拱卫着经济学家的宗教权力。</p><h3 id="h-" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">对新古典经济学的康德式批判</h3><p>作者敏锐地捕捉到了新古典经济学的社会主义倾向。即要超越人的主观性，在人之外寻找到客观尺度，以确立客观真理（俗称的唯物主义）。新古典经济学找到的尺度是市场所表达的效率原则。其所否定的，即是做为主体的人动用自己的主观理性能力建立的普遍秩序的可能性。市场的超级智能来自演化，来自冥冥，而不来自组成市场的人。</p><p>在“摒弃主观秩序、谋求客观标准”这根本点上，新古典经济学与其上一个版本——古典经济学，在内核上是完全一致的。古典经济学摒弃主观效用对价值的解释，而寻求价值的客观标准，终于建立劳动价值论的体系。他们妄图像用尺子度量长度一样，用客观的生产成本（最终还原为劳动）来度量价值。这样的体系下价值仅仅变成人的血酬，人的主观感受日益被挤压到边缘，而官僚机构开始凌架其上成为事实的价值标准的制定者。于是我们可以看到社会主义国家对粗钢产量、超级工程、巨大城市、全产业链等病态式的追求，而个体的福祉成为最不重要的细枝末节。</p><p>劳动价值理论不能解决基本的价值悖论，它的大厦在主观价值论的边际革命的猛烈进攻下轰然倒塌。但是古典经济学的社会主义幽灵经马歇尔的综合而重新集结为新古典经济学。起初它只是一个把主观效用和成产生本拼装起来的杂种，但经过一百年的发展，通过对信息经济学的研究，终于再次找到外部世界的客观标准——神秘的市场效率（试问有比有效市场假说更灵异的学说吗？）。新古典经济学固然是新的，但是并未摆脱古典二字，其原由可能正是来自这种试图在人的观念之外寻找秩序的陈旧思路。</p><h3 id="h-" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">未来之路</h3><p>未来新古典经济学的幼稚理路终将被丢弃。从人的主体选则的原则出发建构市场秩序的经济学说会重新发扬光大。实际上我们已经看到前路的光亮。我们看到以奥地利学派思想为指导的加密货币市场正迅速发展起来。通过去除中介、实现无信任交易的区块链技术，经济主体正企图摆脱所有类型的奴役，建立全新的观念秩序。</p>]]></content:encoded>
            <author>aulee@newsletter.paragraph.com (Aulee)</author>
        </item>
        <item>
            <title><![CDATA[读书笔记：Pay To Multisig (P2MS)及其编程实现]]></title>
            <link>https://paragraph.com/@aulee/pay-to-multisig-p2ms</link>
            <guid>NfozTAYSz61XMZFAcLZq</guid>
            <pubDate>Tue, 02 May 2023 16:46:31 GMT</pubDate>
            <description><![CDATA[本文是Programming Bitcoin第8章Bare Multisig一节（143-148页）的读书笔记。 比特币的智能合约语言Script设定了多种标准交易方式，如Pay to PubKey (P2PK)，Pay to Pubkey Hash (P2PKH)，Pay to Multisig (P2MS)，Pay to Script Hash (P2SH)等等。本节讨论P2MS及其编程实现。为什么需要对多个签名的支付（Pay to Multiple Signatures）？其他标准脚本如P2PK，P2PKH，也可以实现在一笔交易中同时向多个公钥转入资金。那么P2MS的必要性又在哪里呢？诚然，对于更基本的P2PK，P2PKH等，一笔交易可以同时有多个inputs和outputs。但是每一个output只包含一个公钥，因而对应的input只包含一个签名。因此，当由于某个原因相应的私钥丢失了，则放入该output中的资金将永远沉没。为了规避这一风险，P2MS在一个output中包含多个公钥（n个），而在对应的input中包含多个签名（m个），但是所需要的签名数少于公钥数（m < ...]]></description>
            <content:encoded><![CDATA[<p>本文是<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://book.douban.com/subject/30441608/">Programming Bitcoin</a>第8章Bare Multisig一节（143-148页）的读书笔记。</p><p>比特币的智能合约语言Script设定了多种标准交易方式，如Pay to PubKey (P2PK)，Pay to Pubkey Hash (P2PKH)，Pay to Multisig (P2MS)，Pay to Script Hash (P2SH)等等。本节讨论P2MS及其编程实现。</p><h3 id="h-pay-to-multiple-signatures" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">为什么需要对多个签名的支付（Pay to Multiple Signatures）？</h3><p>其他标准脚本如P2PK，P2PKH，也可以实现在一笔交易中同时向多个公钥转入资金。那么P2MS的必要性又在哪里呢？诚然，对于更基本的P2PK，P2PKH等，一笔交易可以同时有多个inputs和outputs。但是每一个output只包含一个公钥，因而对应的input只包含一个签名。因此，当由于某个原因相应的私钥丢失了，则放入该output中的资金将永远沉没。为了规避这一风险，P2MS在一个output中包含多个公钥（n个），而在对应的input中包含多个签名（m个），但是所需要的签名数少于公钥数（m &lt; n）。因此当有个别私钥丢失时，仍然可以完成对签名的验证。</p><p>P2MS的应用不止于此。根据<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/bitcoin/bips/blob/master/bip-0011.mediawiki">这里</a>，它还可以作为争端解决的机制。例如，2-of-3脚本可应用于三方托管（three-party escrow）交易。交易的三方包括买方，卖方和仲裁。买方、卖方和仲裁各提供一个公钥。买方向2-of-3交易中存入资金，并向卖方和仲裁发送交易的id。卖方在履行义务后，要求买方共同签署向卖方支付的交易，以将上一交易中绑定的资金发送给卖方。如果买方和卖方不能达成一致意见，则由代理方选择是与买方还是卖方合作签署交易，从而将资金发送给买方或卖方。</p><h3 id="h-p2ms" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">P2MS脚本的设定</h3><p>在我之前所做的关于<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://mirror.xyz/0x78874f895B96BEc9f48e67BAE188309D285b45a0/VI2BBG1LwligU3MqIMY03U1kmUtEn8cabjP_unB5qgg">签名与验证的读书笔记</a>中，我指出数字签名与验证技术可以应用于确立对物的所有权和支配权。而比特币独特的对于交易的定义以及其智能合约脚本语言Script对数字签名与验证技术的运用又进一步把所有权的确立和支配权的确立分解为两个相互联系的过程。笔特币的一笔交易（transaction, tx）由inputs和outputs两部分组成。交易的每一个input都指向之前的某一交易的output。质言之，前一笔交易的output体现对资金所有权的授予，后一笔交易的input体现对资金支配权的取得。每个output锁定的资金仅能由一个input解锁。 而在同一笔交易中，input中所取得的资金支配权的又使得output中对资金所有权的转让成为可能。这样，各交易的input和output得以首尾相连，循环无端，如下图所示：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/829a7f5c718c489fe91ae840b8aaefbefdd1793fc46377a726ba44c1fb20352f.png" alt="交易的链条" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">交易的链条</figcaption></figure><p>相应地交易的智能合约脚本分为两部分：output包含的ScriptPubkey和input包含的ScriptSig：</p><p><strong>ScriptPubkey:</strong> <code>op_m pubkey1 pubkey2 … pubkeyn op_n op_checkmultisig</code></p><p><strong>ScriptSig</strong>: <code>op_0 sig1 sig2 … sigm</code></p><p>将两个脚本合并，得到：</p><pre data-type="codeBlock" text="op_0 sig1 sig2 ... sigm op_m pubkey1 pubkey2 ... pubkeyn op_n op_checkmultisig
"><code>op_0 sig1 sig2 ... sigm op_m pubkey1 pubkey2 ... pubkeyn op_n op_checkmultisig
</code></pre><p>该脚本语言属于类似Forth语言的堆栈语言。每一命令向堆栈推送或（同时）从堆栈抽取元素。在上述角本中，前m+n+3个命令推送m+n+3个元素到堆栈上：</p><pre data-type="codeBlock" text="0 sig1 sig2 … sigm m pubkey1 pubkey2 … pubkeyn n
"><code></code></pre><h3 id="h-checkmultisig" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">定义checkmultisig函数</h3><p>最后一个命令<code>op_checkmultisig</code>则从堆栈取下m+n+3个元素以验证签名。<code>op_checkmultisig</code>是以上述堆栈中的元素为变量的函数。但是，Script语言不是图灵完备的。为完成验证，除了堆栈上的n个公钥和m个签名外，还需要堆栈外的签名哈希（signature hash）<code>z</code>。因此<code>op_checkmultisig</code>函数的变量有两个：<code>stack</code>和<code>z</code>。</p><pre data-type="codeBlock" text="def op_checkmultisig(stack, z):
    if len(stack) &lt; 1:
        return False
    n = decode_num(stack.pop())
    if len(stack) &lt; n + 1:
        return False
    sec_pubkeys = []
    for _ in range(n):
        sec_pubkeys.append(stack.pop())
    m = decode_num(stack.pop())
    if len(stack) &lt; m + 1:
        return False
    der_signatures = []
    for _ in range(m):
        der_signatures.append(stack.pop()[:-1])
    stack.pop()  # take care of the off-by-one error
    count = 0  # tally of valid signatures
    try:
        points = [S256Point.parse(sec) for sec in sec_pubkeys]
        sigs = [Signature.parse(der) for der in der_signatures]
        for sig in sigs:
            if len(points) == 0:
                return False
            while points:
                point = points.pop(0)
                if point.verify(z, sig):
                    count += 1
                    break
        if count == m:  # tally of valid signatures should be equal to m
            stack.append(encode_num(1))
    except(ValueError, SyntaxError):
        return False
    if count == m:
        return True
    else:
        return False
"><code>def op_checkmultisig(stack, z):
    <span class="hljs-keyword">if</span> len(stack) <span class="hljs-operator">&#x3C;</span> <span class="hljs-number">1</span>:
        <span class="hljs-keyword">return</span> False
    n <span class="hljs-operator">=</span> decode_num(stack.<span class="hljs-built_in">pop</span>())
    <span class="hljs-keyword">if</span> len(stack) <span class="hljs-operator">&#x3C;</span> n <span class="hljs-operator">+</span> <span class="hljs-number">1</span>:
        <span class="hljs-keyword">return</span> False
    sec_pubkeys <span class="hljs-operator">=</span> []
    <span class="hljs-keyword">for</span> <span class="hljs-keyword">_</span> in range(n):
        sec_pubkeys.append(stack.<span class="hljs-built_in">pop</span>())
    m <span class="hljs-operator">=</span> decode_num(stack.<span class="hljs-built_in">pop</span>())
    <span class="hljs-keyword">if</span> len(stack) <span class="hljs-operator">&#x3C;</span> m <span class="hljs-operator">+</span> <span class="hljs-number">1</span>:
        <span class="hljs-keyword">return</span> False
    der_signatures <span class="hljs-operator">=</span> []
    <span class="hljs-keyword">for</span> <span class="hljs-keyword">_</span> in range(m):
        der_signatures.append(stack.<span class="hljs-built_in">pop</span>()[:<span class="hljs-number">-1</span>])
    stack.<span class="hljs-built_in">pop</span>()  # take care of the off<span class="hljs-operator">-</span>by<span class="hljs-operator">-</span>one <span class="hljs-function"><span class="hljs-keyword">error</span>
    <span class="hljs-title">count</span> = 0  # <span class="hljs-title">tally</span> <span class="hljs-title">of</span> <span class="hljs-title">valid</span> <span class="hljs-title">signatures</span>
    <span class="hljs-title"><span class="hljs-keyword">try</span></span>:
        <span class="hljs-title">points</span> = [<span class="hljs-title">S256Point</span>.<span class="hljs-title">parse</span>(<span class="hljs-params">sec</span>) <span class="hljs-title"><span class="hljs-keyword">for</span></span> <span class="hljs-title">sec</span> <span class="hljs-title">in</span> <span class="hljs-title">sec_pubkeys</span>]
        <span class="hljs-title">sigs</span> = [<span class="hljs-title">Signature</span>.<span class="hljs-title">parse</span>(<span class="hljs-params">der</span>) <span class="hljs-title"><span class="hljs-keyword">for</span></span> <span class="hljs-title">der</span> <span class="hljs-title">in</span> <span class="hljs-title">der_signatures</span>]
        <span class="hljs-title"><span class="hljs-keyword">for</span></span> <span class="hljs-title">sig</span> <span class="hljs-title">in</span> <span class="hljs-title">sigs</span>:
            <span class="hljs-title"><span class="hljs-keyword">if</span></span> <span class="hljs-title">len</span>(<span class="hljs-params">points</span>) == 0:
                <span class="hljs-title"><span class="hljs-keyword">return</span></span> <span class="hljs-title">False</span>
            <span class="hljs-title"><span class="hljs-keyword">while</span></span> <span class="hljs-title">points</span>:
                <span class="hljs-title">point</span> = <span class="hljs-title">points</span>.<span class="hljs-title">pop</span>(<span class="hljs-params"><span class="hljs-number">0</span></span>)
                <span class="hljs-title"><span class="hljs-keyword">if</span></span> <span class="hljs-title">point</span>.<span class="hljs-title">verify</span>(<span class="hljs-params">z, sig</span>):
                    <span class="hljs-title">count</span> += 1
                    <span class="hljs-title"><span class="hljs-keyword">break</span></span>
        <span class="hljs-title"><span class="hljs-keyword">if</span></span> <span class="hljs-title">count</span> == <span class="hljs-title">m</span>:  # <span class="hljs-title">tally</span> <span class="hljs-title">of</span> <span class="hljs-title">valid</span> <span class="hljs-title">signatures</span> <span class="hljs-title">should</span> <span class="hljs-title">be</span> <span class="hljs-title">equal</span> <span class="hljs-title">to</span> <span class="hljs-title">m</span>
            <span class="hljs-title">stack</span>.<span class="hljs-title">append</span>(<span class="hljs-params">encode_num(<span class="hljs-params"><span class="hljs-number">1</span></span>)</span>)
    <span class="hljs-title">except</span>(<span class="hljs-params">ValueError, SyntaxError</span>):
        <span class="hljs-title"><span class="hljs-keyword">return</span></span> <span class="hljs-title">False</span>
    <span class="hljs-title"><span class="hljs-keyword">if</span></span> <span class="hljs-title">count</span> == <span class="hljs-title">m</span>:
        <span class="hljs-title"><span class="hljs-keyword">return</span></span> <span class="hljs-title">True</span>
    <span class="hljs-title"><span class="hljs-keyword">else</span></span>:
        <span class="hljs-title"><span class="hljs-keyword">return</span></span> <span class="hljs-title">False</span>
</span></code></pre><p>上述代码有两点值得注意：</p><ol><li><p>签名的顺序必须和公钥的顺序相一致。比如一个2-of-3脚本，三个公钥分别为pubkey1, pubkey2, 和pubkey3。签名为sig1和sig3。如果在脚本中签名的排序为sig3 sig1。则sig3将消耗掉pubkey1和pubkey2，使得sig1不能得到验证。</p></li><li><p>原书Exercise 1（第148页）的参考答案似有不确之处。其给出的代码没有对验证正确的签名进行记数并与m比对。例如一个2-of-3脚本，给出的签名为sig1和sig3。如果不比对记数，即使sig3并不正确也能通过验证。关于这一点请参考<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://learnmeabitcoin.com/technical/p2ms">这里</a>。</p></li></ol><h3 id="h-" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">参考书目</h3><p>Song, Jimmy. <em>Programming bitcoin: Learn how to program bitcoin from scratch</em>. O&apos;Reilly Media, 2019.</p>]]></content:encoded>
            <author>aulee@newsletter.paragraph.com (Aulee)</author>
        </item>
    </channel>
</rss>