<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Luoh Ren-Shan (LCamel) on Medium]]></title>
        <description><![CDATA[Stories by Luoh Ren-Shan (LCamel) on Medium]]></description>
        <link>https://medium.com/@lcamel?source=rss-b022690e71d5------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*4EYgppFBVI-RSC9bFMWAxQ.jpeg</url>
            <title>Stories by Luoh Ren-Shan (LCamel) on Medium</title>
            <link>https://medium.com/@lcamel?source=rss-b022690e71d5------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 15 Nov 2026 07:07:18 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@lcamel/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[LazyTower: An O(1) Replacement for Incremental Merkle Trees]]></title>
            <link>https://medium.com/taipei-ethereum-meetup/lazytower-an-o-1-replacement-for-incremental-merkle-trees-94d5a522dd89?source=rss-b022690e71d5------2</link>
            <guid isPermaLink="false">https://medium.com/p/94d5a522dd89</guid>
            <category><![CDATA[zero-knowledge-proofs]]></category>
            <category><![CDATA[merkle-tree]]></category>
            <dc:creator><![CDATA[Luoh Ren-Shan (LCamel)]]></dc:creator>
            <pubDate>Fri, 14 Mar 2025 01:32:29 GMT</pubDate>
            <atom:updated>2025-03-14T01:32:29.571Z</atom:updated>
            <content:encoded><![CDATA[<p>LazyTower 是一個資料結構. 它的用途和 Incremental Merkle Tree (IMT) 一樣: 讓使用者可以逐步地 append items, 並且適用於 zero-knowledge 的 proof of membership.</p><p>LazyTower append 一個 item 的 amortized cost 是 O(1).</p><p>Proof 的 circuit complexity 是 O(log N). Verification cost 是 O(1).</p><h3>核心觀念</h3><p>Items 會從最底下的 level 被 append. 如果一層滿了, 就把整層 digest() 起來並 append 到上層. 這樣下層就又有空間可以容納新的 items. 我們以 width = 4 的 tower 為例, 請看影片:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fpoe.shuhuigeng.workers.dev%3A443%2Fhttps%2Fwww.youtube.com%2Fembed%2F3QeJgxB9ZiQ%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fpoe.shuhuigeng.workers.dev%3A443%2Fhttps%2Fwww.youtube.com%2Fwatch%3Fv%3D3QeJgxB9ZiQ&amp;image=https%3A%2F%2Fpoe.shuhuigeng.workers.dev%3A443%2Fhttps%2Fi.ytimg.com%2Fvi%2F3QeJgxB9ZiQ%2Fhqdefault.jpg&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/e6e31409a1ab6aa1bde5a08a157b1356/href">https://medium.com/media/e6e31409a1ab6aa1bde5a08a157b1356/href</a></iframe><p>藉由儲存</p><pre>digest([0, 1, 2, 3])</pre><p>這個值, 我們可以在之後證明 item 0 1 2 3 曾經被 append 過.</p><p>藉由儲存</p><pre>digest([digest([0, 1, 2, 3]),<br>        digest([4, 5, 6, 7]),<br>        digest([8, 9, 10, 11]),<br>        digest([12, 13, 14, 15])])</pre><p>這個值, 我們可以在之後證明 item 0 … 15 曾經被 append 過.</p><p>我們可以觀察到, tower 裡面儲存的每一格都是一個 Merkle root. 分別固定了 4⁰, 4¹, 4² … 個 item.</p><p>這一點可以從以下影片看的更清楚:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fpoe.shuhuigeng.workers.dev%3A443%2Fhttps%2Fwww.youtube.com%2Fembed%2F7MsGTO6CuqI%3Ffeature%3Doembed%26start%3D285&amp;display_name=YouTube&amp;url=https%3A%2F%2Fpoe.shuhuigeng.workers.dev%3A443%2Fhttps%2Fwww.youtube.com%2Fwatch%3Fv%3D7MsGTO6CuqI&amp;image=https%3A%2F%2Fpoe.shuhuigeng.workers.dev%3A443%2Fhttps%2Fi.ytimg.com%2Fvi%2F7MsGTO6CuqI%2Fhqdefault.jpg&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/50cc346c5b54c1dbf7838eb865c7e090/href">https://medium.com/media/50cc346c5b54c1dbf7838eb865c7e090/href</a></iframe><h3>Cost</h3><p>平均來說, 最下層在每 1 次 append item 會被修改到一次.<br>上一層在每 4 次 append item 會被修改到一次.<br>再上一層在每 16 次 append item 會被修改到一次.</p><p>由於每一層的寬度都相同, 操作也一樣, 所以如果最下層 append 的 cost (save 或 load+hash) 不會超過一個 constant C 的話, 每一個單層修改的 cost 也都不會超過同一個 C.</p><p>這樣平均來說, 一次 append 的 cost 不會超過</p><pre>1 * C  +  1/4 * C  +  1/16 * C  + ...<br>= 1.333 C</pre><h3>Privacy</h3><p>當我們想證明某個 item 曾經被 append 過, 但是又不想直接揭露是哪個 item 時, 可以用 zero-knowledge proof 來證明其 membership.</p><p>我們可以用傳統的 Merkle proof 證明一個 item 屬於 tower 中的某個單一的 root. 比方說: “我的 item 隱藏在 level 10 的第二個 root 的 4¹⁰ 個 item 之中”.</p><p>如果我們只 load 一個 root 來證明 membership 的話, 雖然不會一下暴露是明確的哪一個 item, 但範圍就會從全部的可能幾千萬個縮小到這個 root 下的 4¹⁰ 個了. 當我們希望保留完整的 privacy 時, 則應該把 tower 中所有的 roots 都 load 起來, 再證明其 membership. 但這樣需要 O(log N) 的 cost, 並不理想.</p><p>我們能不能用一個單一的值來 “固定” 這些 roots, 使得證明 membership 時只要 load 一個值就好呢? 也就是說, 我只要 load 這個值, 則當別人舉出一個 root 時, 我就可以確認這個 root 是原本 tower 眾多 root 中的一個呢?</p><p>我們分成 “水平/垂直” 兩階段來改進.</p><h3>改進一: 水平的 level digests</h3><p>前面提到, 當一個 level 裝滿時, 我們會將其 digest 起來存到上層去, 比方說用 digest([0, 1, 2, 3]) 這個值來 “固定” 0, 1, 2, 3. 如果我們選用的 digest function 可以 incremental 地計算, 則我們可以儲存最新的 digest 就好, 不用儲存 array of roots. 比方說, 我們可以採用 Merkle-Damgård construction, 搭配一個 ZK-friendly 的 hash function (如Poseidon hash):</p><pre>digest([a]) = a<br>digest([a, b]) = H(a, b)<br>digest([a, b, c]) = H(H(a, b), c)<br>digest([a, b, c, d]) = H(H(H(a, b), c), d)<br>digest([a, b, c, d, e]) = H(H(H(H(a, b), c), d), e)<br>...</pre><p>因為每一層都只會從最後面 append, 所以每次都是固定 load / hash / save / update length. 可以 incrementally 而不用從頭計算.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*uEpQHzRDCLyWH09nk78ELg.png" /></figure><p>這樣在證明時, 每一層也只要 load 單一一個 level digest 就好.</p><p>但這樣我們仍然有 O(log N) 個值要 load 進 circuit 裡. 還有改進的空間嗎?</p><h3>改進二: 垂直的 digest of digests</h3><p>對於每個 level 的 digests, 我們可以垂直由上往下計算一個 digest of digests. 用這個值來固定所有 digests.</p><p>把頻繁更動的底層放在尾端, 這樣就可以藉由儲存 prefix 的結果來達成局部更新. 不用每次從頭計算.</p><p>也就是說, 我們會儲存這些值:</p><pre>digest([d4]) = d4<br>digest([d4, d3]) = H(d4, d3)<br>digest([d4, d3, d2]) = H(H(d4, d3), d2)<br>digest([d4, d3, d2, d1]) = H(H(H(d4, d3), d2), d1)<br>digest([d4, d3, d2, d1, d0]) = H(H(H(H(d4, d3), d2), d1), d0)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*M1FcJxBXaBOUafgn5GusdQ.png" /></figure><p>當最下層的 d0 變了的時候, 我們可以不用整個重算 H(H(H(H(d4, d3), d2), d1), d0), 可以 load 先前儲存的 H(H(H(d4, d3), d2), d1), 再從後面局部更新 d0 進去.</p><p>當最下層的 d1 d0 變了的時候, 我們可以不用整個重算 H(H(H(H(d4, d3), d2), d1), d0), 可以 load H(H(d4, d3), d2), 再從後面局部更新 d1 d0 進去.</p><p>單一一層更新的 cost 也會是 constant. (load / hash / save)</p><h3>One to Rule Them All</h3><p>於是我們得到了一個 digest of digests, 可以固定每個 level 的 digest. 每個 level digest 又固定了該 level 的 roots. 每個 root 又固定了那棵 tree 的 leaves, 也就是當初加進去的 items.</p><p>這樣在證明時, 我們只要 load O(1) 的資料就好了.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*TQT6n8kY8WKB0Do3bBFynA.png" /></figure><ol><li>每層的 cost 仍然可以用一個共用的 constant bound 住</li><li>越上層修改的頻率越低 (exponentially)</li></ol><p>因此新增 item 的 amortized cost 仍然是 O(1).</p><h3>實作</h3><p>以下我們觀察實作的結果.</p><p>我們可以看到, 新增 item 的平均 gas 的確很快收斂到 constant. (21000 included)</p><p>而使用 width 較大的 tower, 平均的 gas 也較低, 但有其極限.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*mLVknbjtU5UClfNsbHTeig.png" /></figure><p>和 Incremental Merkle Tree 相比, 即便要容納大量的 items, 使用者也不用擔心 gas 上升. 甚至也不用在一開始就決定容量上限.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*lU8ofH8lFZOz6eg2t8C70Q.png" /></figure><p>雖然提高 width 能稍微降低 gas, 但也會增加證明時的 circuit complexity.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*x6KnDMlkCHpyyzz7OmURTw.png" /></figure><p>Deploy 時通常會將 gas 和 circuit complexity 一起考慮. Width 取 4 到 7 是不錯的選擇.</p><h3>結語</h3><p>LazyTower 平均花費的 gas 是 O(1), Circuit complexity 為 O(log N), 且不用一開始就決定容量. 使用 Merkle Tree 的專案都可以考慮使用 LazyTower.</p><h3>Acknowledgement</h3><p>我在 2023 年初有了初步的想法. 感謝 Ethereum Foundation 的 grant 讓我完成實作. 也感謝 reviewer 們的幫助!</p><p>實作目前由 PSE team 維護. 採 open source license 釋出.</p><ul><li>Javascript: <a href="https://github.com/privacy-scaling-explorations/zk-kit">https://github.com/privacy-scaling-explorations/zk-kit</a></li><li>Solidity: <a href="https://github.com/privacy-scaling-explorations/zk-kit.solidity">https://github.com/privacy-scaling-explorations/zk-kit.solidity</a></li><li>Circom: <a href="https://github.com/privacy-scaling-explorations/zk-kit.circom">https://github.com/privacy-scaling-explorations/zk-kit.circom</a></li></ul><p>感謝 Taipei Ethereum Meetup 的 Martinet Lee 的審閱.</p><p>英文版: <a href="https://ethresear.ch/t/lazytower-an-o-1-replacement-for-incremental-merkle-trees/21683">https://ethresear.ch/t/lazytower-an-o-1-replacement-for-incremental-merkle-trees/21683</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=94d5a522dd89" width="1" height="1" alt=""><hr><p><a href="https://medium.com/taipei-ethereum-meetup/lazytower-an-o-1-replacement-for-incremental-merkle-trees-94d5a522dd89">LazyTower: An O(1) Replacement for Incremental Merkle Trees</a> was originally published in <a href="https://medium.com/taipei-ethereum-meetup">Taipei Ethereum Meetup</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[淺談 Commitment]]></title>
            <link>https://medium.com/taipei-ethereum-meetup/commitment-primer-2194496f2185?source=rss-b022690e71d5------2</link>
            <guid isPermaLink="false">https://medium.com/p/2194496f2185</guid>
            <category><![CDATA[commitment]]></category>
            <dc:creator><![CDATA[Luoh Ren-Shan (LCamel)]]></dc:creator>
            <pubDate>Fri, 30 Sep 2022 01:32:23 GMT</pubDate>
            <atom:updated>2022-09-30T01:32:23.610Z</atom:updated>
            <content:encoded><![CDATA[<p>來看一個丟銅板的問題.</p><h4>平常的丟銅板</h4><p>(一陣討論之後)</p><p>A: … 那不然來丟銅板好了, 猜錯請客.</p><p>B: 好喔. 你猜我丟.</p><p>A: 我猜… 正面.</p><p>B: (丟) 反面.</p><p>A: 好吧, 我請客.</p><h4>隔牆丟銅板</h4><p>上面是平常的版本. 如果場景改成 A B 中間隔著一道牆呢?</p><p>B: 好喔. 你猜我丟.</p><p>A: (喊過去) 我猜正面!</p><p>B: (喊回去) 我丟出反面!</p><p>A: (…好像哪裡怪怪的)</p><p>這裡有個問題: 要是 B 壞心, A 猜正面 B 就說反面, A 猜反面 B 就說正面, 那不是永遠都是 A 請客了? 😅</p><p>來換個方法.</p><p>B: 好喔. 你猜我丟.</p><p>A: (把 “正面” 寫在紙條上, 鎖在小盒子裡, 從牆頭丟過去)</p><p>B: (喊回去) 我丟正面!</p><p>A: (把鑰匙丟過牆頭) “看來你要請客囉!”</p><p>B: (打開盒子看到 “正面”)</p><p>B: 好啦好啦! 我請客!</p><p>或者也可能是:</p><p>A: (把裝著 “正面” 的小盒子丟過去)</p><p>B: (喊回去) 我丟反面!</p><p>A: (把鑰匙丟過牆頭) “好啦好啦! 我請客!”</p><p>為什麼這時候 A B 都甘願了?</p><p>A 想: 先前我不甘願, 是因為 B 先知道我猜什麼的話, 就可以和我唱反調, 讓結果傾向對他有利的那邊. 但是現在他看不到我猜什麼了, 也就不知道要傾向哪一邊, 所以我願意接受這個玩法.</p><p>這個 “看不到” 的特性, 我們把它叫做 “hiding”.</p><p>B想: A 猜什麼已經先鎖在盒子裡了. 只要盒子沒有機關 (比方說 A 聽到正面就丟個能開出正面的鑰匙過來, 聽到反面就丟個能開出反面的鑰匙過來), 不會變來變去, 那我也願意接受這過玩法.</p><p>這個 “不會變來變去” 的特性, 我們把它叫做 “binding”.</p><p>A 把箱子丟過去, 這個動作我們把它叫 commit, 而裝著紙條的箱子叫做 commitment.</p><p>A 把鑰匙丟過去, B 打開相信了某件事, 這件事的內容(“A猜正面”)叫做 opening, 而鑰匙則是 proof.</p><h4>隔網路丟銅板</h4><p>場景再改一改: 如果 A 和 B 不是隔著牆, 而是隔著網路呢?</p><p>這時候 “盒子” 就有很多種設計了. 我們先想一個基本的: hash.</p><p>正面 0, 反面 1.</p><p>如果 A 只是直接拿 hash(0), hash(1) 給 B 看, 固定就是那兩個值的話, B 一看就知道了, 這樣沒有 hiding 的效果. 所以可以讓 A 先從很大的範圍 random 取個整數 x, 偶數正面奇數反面, 把 hash(x) 當成 commitment 送給 B. 這樣 B 拿到範圍也很大的 hash(x) 時, 應該猜不出 x 的奇偶 (hiding), 也就不能作怪.</p><p>再來 B 投了銅板之後, A 把 x 傳給 B, 讓 B 去 hash 確認. B 為什麼會甘願呢? 因為 B 相信目前沒有人能夠輕易地找出另一個 x’, 讓 x’ 也 hash 到原先的 hash(x), 也就是 A 沒辦法在聽到 B 說什麼之後又變來變去 (binding), 所以 B 也甘願.</p><p>另一種方法是: A 拿 0 或 1 配上一個 random number 一起去 hash, 將來再把 0 1 和 random number 開給 B 看, 也有一樣的效果.</p><h4>Merkle Tree: Hash 又 Hash</h4><p>再推遠一點: 前面 A 給 B 一個 commitment 之後, 後來可以讓 B 願意相信單一某件事 (單個 opening). 那, 有沒有辦法讓 B 相信多件事呢? (多個 opening)</p><p>假設 A 想後來讓 B 相信 p q r s. 最簡單的就是先把 hash(p, q, r, s) 送給 B, 之後再把 p q r s 給 B 看, B 就會相信. 但是有些應用我們不想讓 B 一次知道那麼多, 或者 p q r s 很多很大, 不想傳那麼巨量的資料給 B 的話, 我們可以改用另一個設計: Merkle Tree.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ZRK76oWAYx7EmX-gAgfLdA.png" /></figure><p>A 先拿 ROOT 當成 commitment 送給 B.</p><p>之後如果 A 想讓 B 相信 q , 那 A 可以提供 q, HP, HHRHS 給 B. 讓 B 自己先計算出 HQ, HHPHQ . 然後從上往下看:</p><p>A: 我說 HHPHQ 沒有變來變去, 你相信嗎?</p><p>B: 嗯… 我相信. 因為目前沒有人可以在固定 hash(y, z) 的值的狀況下, 給出另一組 y’ z’ 讓他也 hash 到 hash(y, z). 所以在固定 ROOT 的值的狀況下, 現在的 HHPHQ 和 HHRHS 應該是你先前算好沒有變來變去的. 好, 我相信 HHPHQ.</p><p>A: 那我說 HQ 沒有變來變去, 你相信嗎?</p><p>B: 嗯… 我相信. 因為目前沒有人可以在固定 hash(y, z) 的值的狀況下, 給出另一組 y’ z’ 讓他也 hash 到 hash(y, z). 所以在固定 HHPHQ 的值的狀況下, 現在的 HP 和 HQ 應該是你先前算好沒有變來變去的. 我相信 HQ.</p><p>A: 那我說 q 沒有變來變去, 你相信嗎?</p><p>B: 嗯… 我相信. 因為目前沒有人可以在固定 hash(x) 的值的狀況下, 給出另一個 x’ 讓他也 hash 到 hash(x). 所以固定了 HQ, 我相信 q 沒有變來變去.</p><p>所以, 如果有 N 個資訊想要將來讓 B 相信, A 可以把這個 hash 又 hash 的 tree 的 root 給 B 當成 commitment 固定住. 將來 A 想要和 B 開箱下面某個單一值的時候, 不用把所有底下的值 (p q r s) 都露給 B 看, B 就會願意相信 A, 且 A 給的 proof 只要大約 log(N) 的大小.</p><h4>多項式</h4><p>數學家還弄出了這樣的東西 (比方說 KZG10 演算法): 如果 A 想要讓 B 將來相信 x1, x2 … xN 能對應到 y1, y2 … yN, 那麼 A 可以造出一個穿過 (x1, y1), (x2, y2) … (xN, yN) 的多項式 P, 也就是 P(x1) = y1, P(x2) = y2 … P(xN) = yN, 然後給 B 一個關於這個多項式的 commitment 固定住. 將來有需要的時候, A 可以單獨開某個點, 讓 B 願意相信比方說 P(x3) = y3, 而且 B 不會因此知道其他點的資訊.</p><p>這邊 A 給的 commitment 和 Merkle tree 的做法一樣好, 是和 N 無關的常數大小. 而 A 每次給 B 的 proof 則比 Merkle tree 的 log(N) 厲害, 只要常數大小的資料就可以做到. 甚至一次開多個點所需的額外 proof 也只要常數大小. 因此對一些很計較大小的場合特別有用.</p><h4>尾聲</h4><p>從丟銅板的例子中, 我們可以體會到 hiding / binding 的動機, 也看到一些不同 commitment 方式的實作. 希望這個例子將來可以幫助你回想起這些觀念.</p><p><em>(感謝 </em><a href="https://medium.com/u/a994cfc48b5a"><em>吳秉宸</em></a><em> 與 </em><a href="https://medium.com/u/5c031577a87d"><em>Chih-Cheng Liang</em></a><em> 的審閱與建議)</em></p><h4>參考資料:</h4><p><a href="http://users.cms.caltech.edu/~vidick/teaching/101_crypto/Blum81_CoinFlipping.pdf">Blum81</a>: Coin Flipping by Telephone: A Protocol for Solving Impossible Problems</p><p><a href="https://amturing.acm.org/award_winners/blum_4659082.cfm">Manuel Blum</a></p><p><a href="https://www.iacr.org/archive/asiacrypt2010/6477178/6477178.pdf">KZG10</a>: Constant-Size Commitments to Polynomials and Their Applications</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=2194496f2185" width="1" height="1" alt=""><hr><p><a href="https://medium.com/taipei-ethereum-meetup/commitment-primer-2194496f2185">淺談 Commitment</a> was originally published in <a href="https://medium.com/taipei-ethereum-meetup">Taipei Ethereum Meetup</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[淺談 Commitment]]></title>
            <link>https://medium.com/@lcamel/%E6%B7%BA%E8%AB%87-commitment-f757a353a89d?source=rss-b022690e71d5------2</link>
            <guid isPermaLink="false">https://medium.com/p/f757a353a89d</guid>
            <dc:creator><![CDATA[Luoh Ren-Shan (LCamel)]]></dc:creator>
            <pubDate>Wed, 07 Sep 2022 07:20:03 GMT</pubDate>
            <atom:updated>2022-09-30T08:19:05.687Z</atom:updated>
            <content:encoded><![CDATA[<p>修改過後的版本在<a href="https://medium.com/taipei-ethereum-meetup/commitment-primer-2194496f2185">這裡</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f757a353a89d" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[(舊文) 用 CREATE2 搭配 CREATE 原地換 Code]]></title>
            <link>https://medium.com/taipei-ethereum-meetup/create2-and-create-2186fb16ed81?source=rss-b022690e71d5------2</link>
            <guid isPermaLink="false">https://medium.com/p/2186fb16ed81</guid>
            <category><![CDATA[selfdestruct]]></category>
            <category><![CDATA[create2]]></category>
            <dc:creator><![CDATA[Luoh Ren-Shan (LCamel)]]></dc:creator>
            <pubDate>Sun, 28 Aug 2022 01:33:19 GMT</pubDate>
            <atom:updated>2024-09-28T18:24:45.544Z</atom:updated>
            <content:encoded><![CDATA[<p>注意: CREATE2 在 EIP-6780 後已不適用了.</p><p>在<a href="https://medium.com/taipei-ethereum-meetup/create2-77108446ad5b">上一篇</a>中, 我們實驗了如何用 CREATE2 原地換 code. 這篇我們則用 CREATE2 搭配 CREATE 來原地換 code (不用 assembly).</p><h3>觀念</h3><p>廣泛地說, 能原地換 code 的條件是:</p><ol><li>有自爆能力: contract 直接呼叫 SELFDESTRUCT, 或透過不論幾層 CALLCODE / DELEGATECALL 呼叫到 SELFDESTRUCT, 就有能力自爆.</li><li>能重複 deploy 到同一個 address: contract 如果有個祖先是用 CREATE2 生出來的, 而自己和中間的祖先都可自爆的話, 即便這些 contract 只是用普通 CREATE 生出來的, 也會因為 parent 的 nonce 被自爆重置而可以重複 deploy 到相同的 address, 產生原地換 code 的效果.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vFaw92rmcpsMbi3b5460-w.png" /></figure><h3>觀念細節</h3><p>以上圖為例: contract C2 含有 SELFDESTRUCT 所以可以直接自爆, 而 C1 C3 因為能讓 SELFDESTRUCT 在其 address 發生, 所以即便 contract 裡面沒有 SELFDESTRUCT 也能間接自爆.</p><p>自爆後, 要怎麼重複 deploy 到同一個 address 呢? 我們回顧一下決定 contract address 的規則:</p><pre>傳統 CREATE / contract creation transaction 的 new contract address:<br>    function addr(sender_address, nonce)</pre><pre>CREATE2 的 new contract address:<br>    function addr(sender_address, salt, init_code)</pre><p>上圖中, C0 生 C1 用的是 CREATE2, 所以第二次只要指定和上次一樣的 init code 和 salt, 就能從 addr0 再次用 CREATE2 deploy 到 addr1. (重要的起點)</p><p>上圖中, C1 生 C2 用的是 CREATE. 由於 CREATE 只看 sender address 和 nonce, 而 addr1 的 nonce 會在自爆後重置, 所以只要 addr1 的 nonce 又遞增到和當初生 C2 時相同的值, 就可以從 addr1 再次用 CREATE deploy 到 addr2.</p><p>同理, 也可以從 addr2 再次用 CREATE deploy 到 addr3.</p><p>C1 C2 C3 重生的內容都可以和原來不一樣.</p><p>而且因為影響 CREATE address 的是 nonce 而不是 init code, 所以重新 deploy 時可以用完全不同的 init code. 也就是在 Solidity 中第二次只要 new 不同的 contract, 就能達到原地換 code 的效果.</p><h3>實作</h3><h4>自爆 (SELFDESTRUCT)</h4><p>上一篇我們用的是直接自爆. 這邊我們確認一下多層 <strong>D</strong>elegate <strong>C</strong>all 間接自爆的效果:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a2d6fe022c0dbe9d756056d93da891fe/href">https://medium.com/media/a2d6fe022c0dbe9d756056d93da891fe/href</a></iframe><p>用<a href="https://gist.github.com/LCamel/bf0f173f06c6651c192b01bd48c4de74#file-dcdcbomb-mjs">這段 code 驅動</a>: 先 deploy DCDCBomb 並給它 value 42, 再 trigger fallback() 間接自爆.</p><p>從輸出來觀察 contract DCDCBomb account 的變化:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a3f8fa340d61f0c41ce2e374b9c717dc/href">https://medium.com/media/a3f8fa340d61f0c41ce2e374b9c717dc/href</a></iframe><p>可以看到 DCDCBomb 的 nonce / balance / storage / code 全都被間接呼叫的 SELFDESTRUCT 清空了, 相當乾淨.</p><h4>重複 deploy 到同一個 address 並換 code</h4><p>前面驗證過了間接自爆, 下面的 code 就回到簡單直接自爆, 把重點放在怎麼重複 deploy 到同一個 address 上.</p><p>我們用 Solidity 實驗如下圖中置換 C2 成 Cafe, C3 成 Babe 的場景:</p><ol><li>生成 C1 -&gt; C2 -&gt; C3</li><li>讓 C1 C2 C3 自爆</li><li>生成 C1 -&gt; Cafe -&gt; Babe 在相同的 addresses 上</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*lYWPxsGi6szuTutpC5K7pw.png" /></figure><p>仔細觀察 Step 3.</p><p>先從 C0 重新 CREATE2 回 C1 : 在上文中我們已經實驗過用 CREATE2 原地換 code, 這次就僅用 CREATE2 生回相同的內容 (所以不用 assembly).重複給一樣的 init code 和 salt, 就能再生到 addr1. 我們可以在 Solidity 中用這樣的句型來叫用 CREATE2 :</p><pre>new C1{salt: 0}()</pre><p>再來把 C2 換成 Cafe: 因為 CREATE 看 nonce 不看 init, 而 addr1 的 nonce 被自爆重置了, 所以這邊簡單把 new C2() 改成 new Cafe() 就能把 addr2 的 code 從 C2 原地換成 Cafe.</p><p>把 C3 換成 Babe 也是一樣的道理: addr2 的 nonce 被重置了, 所以把 new C3() 改成 new Babe() 就能把 addr3 的 code 從 C3 原地換成 Babe.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/9e6450f81c9ad09fc48b64eef1363c37/href">https://medium.com/media/9e6450f81c9ad09fc48b64eef1363c37/href</a></iframe><p>用<a href="https://gist.github.com/LCamel/bf0f173f06c6651c192b01bd48c4de74#file-create2_create-mjs">這段 code</a> 執行上面的 contract, 就可以看到 contract C1 C2 C3 被建立, 被清空, 最後又在原地替換成 contract C1 Cafe Babe:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a8bf503a42598a84aec7107559abafe3/href">https://medium.com/media/a8bf503a42598a84aec7107559abafe3/href</a></iframe><h3>換 code 的影響與應對</h3><p>從以上的例子可以看到, 不光是 CREATE2 生出的 contract 會被原地換 code, 只要滿足自爆和重生的條件, 連 CREATE 生出來的也有機會換 code.</p><p>那麼, 如果我們仔細檢查了某 address 的 source code 是個自動販賣機, 會不會真正投錢進去之後, 才發現把錢丟進了吃角子老虎裡面了呢?</p><p>的確有可能.</p><p>Caller 端應對的方法:</p><p>方法一: 靠 trust: “他們家大業大聲譽好, 一定不會亂搞的!” (?)</p><p>方法二: 仔細檢查 contract 是否能自爆和原地重生, 兩個條件少一個就換不動 code. (檢查起來不見得輕鬆)</p><p>方法三: call contract 前, 在同一個 transaction 中呼叫 EXTCODEHASH 來比對, code hash 符合檢閱過的版本才呼叫. 這樣就算在同一個 block 的前一個 transaction 中被偷換 code 了也能當場發現. (但仍要留心 code 之外的 nonce / balance / storage 可能被重置過)</p><p>而從 contract 撰寫者的角度來看, CREATE2 給了原地升級的方便, 但同時也提高了取信 caller 的難度. 在 governance 面要更加注意.</p><h3>尾聲</h3><p>本文對 CREATE2 和 CREATE 做了些實驗以幫助理解. 沒有討論 CREATE2 換 code 之外的面向(如 state channel), 觀念也沒有超出下面幾篇的範圍. 有興趣可以繼續閱讀下面的幾篇好文.</p><p>全部的 code 在<a href="https://github.com/LCamel/ethereum-snippets/tree/main/medium_init_create2">這裡</a>.</p><p><em>(感謝 </em><a href="https://medium.com/u/84b37154b2d5"><em>Peter Lai</em></a><em> 的審閱與建議)</em></p><ul><li><a href="https://eips.ethereum.org/EIPS/eip-1014">EIP-1014: Skinny CREATE2</a></li><li><a href="https://ethereum-magicians.org/t/potential-security-implications-of-create2-eip-1014/2614">Potential security implications of CREATE2? (EIP-1014)</a></li><li><a href="https://medium.com/taipei-ethereum-meetup/eip-1014-%E7%94%A2%E7%94%9F%E5%8F%AF%E6%8E%A7%E7%9A%84%E6%99%BA%E8%83%BD%E5%90%88%E7%B4%84%E5%9C%B0%E5%9D%80-fd564caa3a1e">EIP-1014 產生可控的智能合約地址</a></li><li><a href="https://blog.openzeppelin.com/the-state-of-smart-contract-upgrades/#metamorphic-contracts">The State of Smart Contract Upgrades - OpenZeppelin blog</a></li><li><a href="https://medium.com/@0age/the-promise-and-the-peril-of-metamorphic-contracts-9eb8b8413c5e">The Promise and the Peril of Metamorphic Contracts</a></li><li><a href="https://medium.com/@jason.carver/defend-against-wild-magic-in-the-next-ethereum-upgrade-b008247839d2">Defend against “Wild Magic” in the next Ethereum upgrade</a></li><li><a href="http://web.archive.org/web/20220119152342/https://swende.se/blog/Ethereum_quirks_and_vulns.html">Ethereum quirks</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=2186fb16ed81" width="1" height="1" alt=""><hr><p><a href="https://medium.com/taipei-ethereum-meetup/create2-and-create-2186fb16ed81">(舊文) 用 CREATE2 搭配 CREATE 原地換 Code</a> was originally published in <a href="https://medium.com/taipei-ethereum-meetup">Taipei Ethereum Meetup</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[(舊文) 用 CREATE2 原地換 Code]]></title>
            <link>https://medium.com/taipei-ethereum-meetup/create2-77108446ad5b?source=rss-b022690e71d5------2</link>
            <guid isPermaLink="false">https://medium.com/p/77108446ad5b</guid>
            <category><![CDATA[itin]]></category>
            <category><![CDATA[create2]]></category>
            <category><![CDATA[selfdestruct]]></category>
            <dc:creator><![CDATA[Luoh Ren-Shan (LCamel)]]></dc:creator>
            <pubDate>Thu, 18 Aug 2022 01:33:33 GMT</pubDate>
            <atom:updated>2024-09-28T18:22:19.796Z</atom:updated>
            <content:encoded><![CDATA[<p>注意: CREATE2 在 EIP-6780 後已不適用了.</p><p>Ethereum 自從引入 opcode CREATE2 後, 同一個 address 可能會換上不一樣的 code. 我們來實驗看看, 如何用 CREATE2 生成 / 自爆(SELFDESTRUCT) / 再原地換上新的 code.</p><h3>New Contract Address</h3><p><a href="https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1014.md">EIP-1014</a> 這麼說:</p><blockquote>Specification</blockquote><blockquote>Adds a new opcode (CREATE2) at 0xf5, which takes 4 stack arguments: endowment, memory_start, memory_length, salt. Behaves identically to CREATE (0xf0), except using keccak256( 0xff ++ address ++ salt ++ keccak256(init_code))[12:] instead of the usual sender-and-nonce-hash as the address where the contract is initialized at.</blockquote><p>CREATE2 多了第四個參數 <em>salt. </em>而 address 也多了對應的新規則 (重要):</p><pre>傳統 CREATE / contract creation transaction 的 new contract address:<br>    function addr(sender_address, nonce)</pre><pre>CREATE2 的 new contract address:<br>    function addr(sender_address, salt, init_code) </pre><p>以往 CREATE / contract creation transaction 因為 nonce 一直遞增, 所以無法重複生在同一個 address.<br>而 CREATE2 沒有用到 nonce! 所以只要我們從同一個 sender 送出 CREATE2 並帶著相同的 salt 和 init_code, 就可以把新的 contract 重複生到同一個 address 上.</p><h3>SELFDESTRUCT 自爆</h3><p>在 CREATE2 重複生到同一個 address 前, contract 要先自爆來空出位子. 這需要該 contract 直接呼叫 opcode SELFDESTRUCT, 或者間接 CALLCODE / DELEGATECALL 到其他含有 SELFDESTRUCT 的 contract.</p><h3>相同的 init, 不同的 contract body</h3><p>雖然我們想換掉 contract body (code), 但是因為 init code 變了 address 就會跟著變, 所以我們必須重用同一段 init code, 並在前後兩次產生不同的 contract body.</p><p>平常 Solidity compile 出來的 init code 是將夾帶的唯一一份 contract body 用 CODECOPY 的方式複製出來, 所以同一份 init code 會生出同一份 body.</p><p>然而 Ethereum 本身並沒有這樣的限制. 生出怎樣的 body 不光取決於 init code 的 binary 而已, 也取決於執行 init 時的 environment 和 state:</p><pre>錯誤的想像:<br>    function body(init_code)</pre><pre>正確的想像:<br>    function body(init_code, environment, state)</pre><p>所以如果 init 參考了 environment (如 value / time / block number), 或者參考了 state (如呼叫別的 contract 的 function 來下載 body, 或動態 EXTCODECOPY 不同的 contract 來用), 即使 init 沒變, 仍然可以生出不同的 body !</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Mdqk_VoEH02_dRDIEcZKJg.png" /></figure><h3>實作</h3><p>我們需要準備兩段 code: 會生出不同 body 的 init, 和發送這段 init 的 CREATE2 factory contract.</p><p>實作細節建議先參考<a href="https://medium.com/taipei-ethereum-meetup/init-f35c7d6aa7a0">前一篇文章: Init.</a></p><h4>Init</h4><p>CREATE2 有個不會影響 address 又容易操控的參數: value (endowment). 為了 demo, 這裡選擇不參考 state, 而是在 init 中參考 value 來動態決定 body:</p><pre>如果 value 是 0, 就生出 bomb contract body “0x3DFF”. 這個 contract 收到任何 message 都會 0xFF SELFDESTRUCT. (並把餘額送到指定的 address 0)</pre><pre>如果 value 不是 0, 就生出另一個 contract body “0xCafeBabe”:</pre><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/f46c770f5269460b92cb67e96830aca2/href">https://medium.com/media/f46c770f5269460b92cb67e96830aca2/href</a></iframe><p>用嵌在 JavaScript 中的<a href="https://gist.github.com/LCamel/97b7be1e36da9c5e914886b135d6769b#file-asm-js">這段 assembler</a> 編一下, 會得到 bytecode 34600F57613DFF6000526002601EF35B63CAFEBABE6000526004601CF3 .</p><p>如果有裝 geth, 可以用 geth 的 command line evm 餵不同的 value 先測看看 (搭配這份 <a href="https://gist.github.com/LCamel/97b7be1e36da9c5e914886b135d6769b#file-g-json">state</a>):</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/e61e66404642f9a3852ed74e4ecc8502/href">https://medium.com/media/e61e66404642f9a3852ed74e4ecc8502/href</a></iframe><h4>Factory</h4><p>有了 init code, 再來需要寫個 factory contract, 用 CREATE2 把上面這段 init 發送到 new contract address 去執行.</p><p>回頭想想, 平常 address 0 雖然沒有實際的 code, 但是概念上就像個通用的 factory contract: 只要我們用 sendTransaction 把 init 當 data 送給它, 它就會把 init 送到目的地去執行(並傳送 value).</p><p>我們也來刻意模仿這個行為, 寫個半通用的 factory contract: 只要我們用 sendTransaction 把 init 當 data 送給它, 它就會把 init 透過 <strong>CREATE2</strong> 送到目的地去執行(並傳送 value).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Hq9SqprWP2Qqm0Yj6L1Eyw.png" /></figure><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/494c9c8949cdbc12e54fb24d5535bd01/href">https://medium.com/media/494c9c8949cdbc12e54fb24d5535bd01/href</a></iframe><h4>RUN !</h4><p>Init 和 factory 準備好, 就可以來 生成 / 自爆 / 再生成新的 code 了:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/44bf81d132299a2979f4b190becc04ae/href">https://medium.com/media/44bf81d132299a2979f4b190becc04ae/href</a></iframe><p>執行結果:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/ca7810bd89b8fe66fdb7870334870d8d/href">https://medium.com/media/ca7810bd89b8fe66fdb7870334870d8d/href</a></iframe><p>可以看到在生成/自爆之後, 因為用了 CREATE2, 所以第二次還是會 deploy 到同一個 address, 而此 address 的 code 也從 0x3dff 換成了 0xcafebabe !</p><p><a href="https://goerli.etherscan.io/address/0x463e91cafe1749e275fc1f4a6f1fe64c1f392463#code">Etherscan</a> 也會在 contract tab 加上紅字 “ReInit” 和文字說明來提醒使用者這個 contract 被換了 code :</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zcIA49zlk310CkvLWzWxzQ.png" /></figure><p>最後驗證一下我們的理解:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/9a6e282023835df4e274371bb8340d70/href">https://medium.com/media/9a6e282023835df4e274371bb8340d70/href</a></iframe><h3>尾聲</h3><p>從上面的例子可以看到, 如果:</p><ol><li>contract 有能力 SELFDESTRUT</li><li>可以重複 deploy 到同一個 address (因為引入了 CREATE2)</li></ol><p>則這個 contract 就可以原地換 code.</p><p>完整的程式碼<a href="https://github.com/LCamel/ethereum-snippets/tree/main/medium_init_create2">在這裡</a>.</p><p>下一篇, 我們將繼續討論換 code 造成的影響, 以及如何用 CREATE 原地換 code.</p><p><em>(感謝 </em><a href="https://medium.com/u/aca73f7679fe"><em>NIC Lin</em></a><em> 與 </em><a href="https://medium.com/u/1f76e2783ed6"><em>Ping Chen</em></a><em> 的審閱與建議)</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=77108446ad5b" width="1" height="1" alt=""><hr><p><a href="https://medium.com/taipei-ethereum-meetup/create2-77108446ad5b">(舊文) 用 CREATE2 原地換 Code</a> was originally published in <a href="https://medium.com/taipei-ethereum-meetup">Taipei Ethereum Meetup</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Init]]></title>
            <link>https://medium.com/taipei-ethereum-meetup/init-f35c7d6aa7a0?source=rss-b022690e71d5------2</link>
            <guid isPermaLink="false">https://medium.com/p/f35c7d6aa7a0</guid>
            <category><![CDATA[assembly]]></category>
            <category><![CDATA[itin]]></category>
            <dc:creator><![CDATA[Luoh Ren-Shan (LCamel)]]></dc:creator>
            <pubDate>Wed, 10 Aug 2022 01:32:44 GMT</pubDate>
            <atom:updated>2022-08-10T07:07:57.196Z</atom:updated>
            <content:encoded><![CDATA[<p>Ethereum 有三種方式建立 contract:</p><ol><li>發送 contract creation transaction, 也就是 “to” address 為 0 的 transaction</li><li>呼叫 opcode CREATE</li><li>呼叫 opcode CREATE2</li></ol><p>這三種方式都能給定一段要在新的 contract address 執行的, 只跑唯一一次的初始化 code, 也就是 <strong>init</strong>. Contract creation transaction 會把整個 “data” 當成 init, 而 CREATE / CREATE2 則是用參數指定 init.</p><h3>Init</h3><p><a href="https://github.com/ethereum/yellowpaper">Yellow paper</a> 這樣說:</p><blockquote><strong>init</strong> is an EVM-code fragment; it <strong>returns</strong> the <strong>body</strong>, a second fragment of code that executes each time the account receives a message call</blockquote><p>Init code 就像傳統 OS 的應用程式 installer 一樣, 負責安裝和初始化. 不過 init 不是把程式寫到硬碟裡, 而是把要裝的 body 寫到 memory 中, 再透過 <strong>RETURN</strong> opcode 指給 EVM 看. EVM 會在 init return 後把這段被指的 memory 寫成將來每次跑的 contract body.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*dlMABL_BQw5NFd7EnBeexg.png" /><figcaption>Generate and RETURN</figcaption></figure><p>我們來寫一段產生 contract body 只有一個 byte 0x42 的 init code:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/37fd87cdc1595adb1bda7244235a4abb/href">https://medium.com/media/37fd87cdc1595adb1bda7244235a4abb/href</a></iframe><p>寫一個非常陽春的, 可嵌入在 JavaScript 中的 assembler 來轉上面的 code:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/e35fdacfe7ae565367d40e5c63d6907d/href">https://medium.com/media/e35fdacfe7ae565367d40e5c63d6907d/href</a></iframe><p>asm(`PUSH1 42 …`) 出來的 bytecode 會是 “604260005360016000F3” .</p><p>如果有裝 geth, geth 有送一個 command line 版的 evm, 可以把上面的 bytecode 丟進去測:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/ad9c9bb274ffe26332a22e1756940f67/href">https://medium.com/media/ad9c9bb274ffe26332a22e1756940f67/href</a></iframe><p>看起來沒問題. 用 sendTransaction 的 data 發送看看:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b6ebd2e7a8a5743b90c3c0bf7ecd6937/href">https://medium.com/media/b6ebd2e7a8a5743b90c3c0bf7ecd6937/href</a></iframe><p>執行結果:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/18aa6255e99f84fe84ea92d7388b8db9/href">https://medium.com/media/18aa6255e99f84fe84ea92d7388b8db9/href</a></iframe><p>getCode() 回來的確是 0x42, 成功了! (<a href="https://goerli.etherscan.io/address/0x4dA201456B2D0a4BB25e208BF8Bc1A2697C17741#code">goerli</a>)</p><h3>CODECOPY</h3><p>上面的範例只 deploy 了一個 byte 0x42. 如果想要 deploy 更長的 code, 常見的做法是: 寫一段 deployer, 把 body 黏在後面, deployer 用 opcode CODECOPY 把整段 body 複製進 memory 中, 再用 RETURN 指給 EVM 看.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*skuZAvlp533Cwb035oD4_A.png" /><figcaption>CODECOPY and RETURN</figcaption></figure><p>舉例來說, deploy 一份 4 bytes 的 body “<a href="https://goerli.etherscan.io/address/0x36de676Ce6e3Aa7B03375d0b2A0509b762bc4BBb#code">0xCafeBabe</a>” 可以這樣做:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/4ec2a053556a945e43494feaefa90945/href">https://medium.com/media/4ec2a053556a945e43494feaefa90945/href</a></iframe><p>Solidity compiler 也是用 CODECOPY 這個做法:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*c4sC7d1mMXSjH0o9FOOOiQ.png" /><figcaption>CODECOPY and RETURN: solc — asm</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*w-OOXf-DSZxLbVq8cioR9w.png" /><figcaption>CODECOPY and RETURN: solc — bin</figcaption></figure><h3>一個便於實驗的 deployer</h3><p>上面我們的 deployer 寫死了 body 4 這個長度. 如果不想每次改 body 就手動改長度的話, 可以用 CODESIZE =deployer + body 減去固定的 deployer 長度, 來得到接在後面的變動的 body 長度:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/385da924397c9583cd74a62e40062539/href">https://medium.com/media/385da924397c9583cd74a62e40062539/href</a></iframe><p>我們常常需要用 PUSH1 00 來 push 一個 0 到 stack 上. 為了榨取一點 gas, 只要還沒 CALL 其他的 contract, 我們就可以用 opcode RETURNDATASIZE (0x3D) 來達成 push 0 的效果. 細心的你可能已經發現到, 在上面的 assembler 中我們定義了 “_MYZERO_” , 一樣是對應到 0x3D, 但取個名字讓放 0 的意圖更明顯.</p><p>有了這段 deployer, 我們就可以直接在後面黏上任何長度的 body, 這樣做各種實驗都會方便許多:</p><pre>var deployer = &quot;600B380380600B3D393DF3&quot;;</pre><pre>deploy(deployer + “CafeBabe”);<br>deploy(deployer + “000000000000DeadBeef”);</pre><h3>尾聲</h3><p>Init code 像是 installer 一樣, 不論是用動態生成的還是用 copy 的, 只要能把 memory 填好 RETURN 給 EVM, 就能 deploy 成 contract body.</p><p>完整的 <a href="https://gist.github.com/LCamel/77a0a03175ca8dd48ec3e17b70cdde36#file-init-mjs">code</a> 和 <a href="https://gist.github.com/LCamel/77a0a03175ca8dd48ec3e17b70cdde36#file-output-txt">輸出</a> 請看連結.</p><p>下一篇, 我們將實驗如何用 CREATE2 原地換 code.</p><p><em>(感謝 </em><a href="https://medium.com/u/aca73f7679fe"><em>NIC Lin</em></a><em> 的審閱與建議)</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f35c7d6aa7a0" width="1" height="1" alt=""><hr><p><a href="https://medium.com/taipei-ethereum-meetup/init-f35c7d6aa7a0">Init</a> was originally published in <a href="https://medium.com/taipei-ethereum-meetup">Taipei Ethereum Meetup</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>