丹評|《#吉娃娃羅曼死》チワワちゃん:
躁動的電音狂響,掩蓋著青春逝去的惆悵
▲網誌版: http://danslecinema.tw/2019/02/chiwawa/
集結犯罪推理、同儕友誼、頹靡愛情與嗨翻舞曲於一身的青春物語,《吉娃娃羅曼死》在日片常見的留白唯美與怪誕超展開之間尋得絕佳平衡,義無反顧地帶觀眾一起在夜店和泳池派對上大喊青春萬歲,同時也讓我們融入小圈圈,在夜深人靜時瞥見角色們隱隱顯露而出的稚嫩成長焦慮。
由《裸睡美人》導演二宮健帶來的作品,取材自岡崎京子的短篇漫畫,故事講述一名迷樣的人氣妹子「吉娃娃」(吉田志織 飾)以及她的夥伴們如何誤打誤撞獲得了大筆錢財,並展開了紙醉金迷的荒唐享樂行程。電影開頭即跳接了吉娃娃驚傳遭分屍的消息,於是由美樹(門脇麦 飾)作為敘事主線,試圖抽絲剝繭瞭解事情的真相,拼湊出吉娃娃生前最真實的模樣。
以夥伴們的口述為引子,來回穿插回憶與當下。探查的旅途最終卻令人恍然大悟,原來尋找的已經不是吉娃娃,而是自己那段不再的青春時光。表面上是推理劇,埋藏其中的卻是滿溢著向過去致敬的青春成長電影,在場景重現的過程中,也彷彿透過電影這個時空膠囊,將這些如煙花燦爛的回憶凍結,交織成一幅令人鼻酸的紀念與別離。
整片以電音舞曲貫穿,舞曲的無窮遞迴和重低音的脈動撞擊,提供了迷幻而鼓譟的青春底韻。與《BPM》異曲同工,震耳欲聾的樂音不僅是全力狂歡的助興劑,也是麻痺孤獨的逃避之處。本片所選的歌曲令人很想與角色們一起狂歡搖擺,可說是近期最優秀的電影歌曲原聲帶(反觀《美麗男孩》那意味不明又塞得過滿的選曲)。片中也不乏舞台劇或音樂劇的元素,一幕吉娃娃與舞群跳唱的 “Television Romance” (https://youtu.be/Zx_ftS-rq8c)有潛力成為下一個《月薪嬌妻》的『恋ダンス』。
剪輯勁到十足,由各種質地與角度的破碎鏡頭,拼湊而出的短促蒙太奇,將為整個都會城市作為歌舞青春的舞台。一幕眾人在東京街頭上拔腿狂奔的開場戲精彩至極,令人不難沾染那無畏不羈的氣息。這種高度風格化的視覺呈現,執行上比起去年的港片《G殺》更為精準,在混亂與抓狂中卻帶有層次與敘事的一致性。攝影畫面充滿各種過曝暈染與高飽和霓紅色彩等迷幻元素,但也少不了令人醉心的唯美畫面(包含泳池的擁吻與碼頭邊的道別)。一干演員也都表現亮眼,特別是兩位女主角(一個像孫儷一個像柯佳嬿)。吉田志織看不出僅是第二部長片演出,完美演繹甜美可愛卻帶有距離感的迷樣女子。門脇麦的落寞與自卑,則是推動整個電影前進的情緒核心。
嚴格來說,這部電影仍然有一些問題。原本的犯罪偵探情節,在片頭重重舉起,到後段卻輕輕放下、草草了結。以大尺度電影自居,在部分鏡頭仍顯扭捏,似乎還是有放不下的包袱、不夠灑脫。眾多角色們逐一述說他們各自眼中的故事時,卻沒有互補或互斥的戲劇堆疊效果,這樣輪番下來就顯得有些重複。而最主要的則是前半段的狂歡畫面,有些畫面逗留太久、慢動作氾濫,彷彿是好不容易搭了一個大場面,就不小心陷入自溺中,缺乏推動劇情的動能時,就會有在看 MV 的空洞感。若是這些比較躁動的鏡頭,可以多留一些給後半那種靜謐的場景,那或許角色的連結與深度會更加彰顯出來。
最後一行人來到了海邊,向逝去的吉娃娃道別。繞了一大圈,仍無人得知女主角究竟是如何死去。也許吉娃娃就是我們的青春,前些日子仍活蹦亂跳、光彩奪目,卻不知曾幾何時已黯然退場。角色們紛紛拿起 V8 自拍的呢喃絮語,神來一筆地打破第四牆、模糊了紀實與虛構。彷彿這些角色就是我們自己的校園死黨們,而我們也跟著他們一起透過電影走了一趟青春的回憶長廊。我們的靈魂都在都市巷弄中孤寂流連,為了追尋一些什麼,到頭來卻是一場空。過去總是不堪回首又充滿悔悟,現在則是在混沌與迷茫中困窘著,但不管如何,未來始終微微探著亮光。動茲動茲動茲,那是青春的送葬進行曲,讓我們再一同隨著節拍舞上一回。
-
預告片:
https://youtu.be/35RM4badwpk
電影主題曲 Have a Nice Day! -「僕らの時代」:
https://open.spotify.com/track/0sRZ7Rn4DzXjKg8Xfikkd8
電影原聲帶:
https://open.spotify.com/album/1KCaca4z2RX1apK14nLYvK
2/27 連假發行
采昌國際多媒體 采昌亞洲電影世界 CAI CHANG Asia
遞迴 樹 高度 在 Taipei Ethereum Meetup Facebook 八卦
📜 [專欄新文章] Merkle Tree in JavaScript
✍️ Johnson
📥 歡迎投稿: https://medium.com/taipei-ethereum-meetup #徵技術分享文 #使用心得 #教學文 #medium
這篇文章會說明 Merkle Tree 的運作原理,以及解釋 Merkle Proofs 的用意,並以 JavaScript / TypeScript 簡單實作出來。
本文為 Tornado Cash 研究系列的 Part 1,本系列以 tornado-core 為教材,學習開發 ZKP 的應用,另兩篇為:
Part 2:ZKP 與智能合約的開發入門
Part 3:Tornado Cash 實例解析
Special thanks to C.C. Liang for review and enlightenment.
本文中實作的 Merkle Tree 是以 TypeScript 重寫的版本,原始版本為 tornado-core 以 JavaScript 實作而成,基本上大同小異。
Merkle Tree 的原理
在理解 Merkle Tree 之前,最基本的先備知識是 hash function,利用 hash 我們可以對資料進行雜湊,而雜湊後的值是不可逆的,假設我們要對 x 值做雜湊,就以 H(x) 來表示,更多內容可參考:
一次搞懂密碼學中的三兄弟 — Encode、Encrypt 跟 Hash
SHA256 Online
而所謂的 Merkle Tree 就是利用特定的 hash function,將一大批資料兩兩進行雜湊,最後產生一個最頂層的雜湊值 root。
當有一筆資料假設是const leaves = [A, B, C, D],我們就用function Hash(left, right),開始製作這顆樹,產生H(H(A) + H(B))與H(H(C) + H(D)),再將這兩個值再做一次 Hash 變成 H(H(H(A) + H(B)) + H(H(C) + H(D))),就會得到這批資料的唯一值,也就是 root。
本文中使用的命名如下:
root:Merkle Tree 最頂端的值,特色是只要底下的資料一有變動,root 值就會改變。
leaf:指單一個資料,如 H(A)。
levels:指樹的高度 (height),以上述 4 個資料的假設,製作出來的 levels 是 2,levels 通常會作為遞迴的次數。
leaves:指 Merkle Tree 上的所有資料,如上述例子中的 H(A), H(B), H(C), H(D)。leaves 的數量會決定樹的 levels,公式是 leaves.length == 2**levels,這段建議先想清楚!
node:指的是非 leaves 也非 root 的節點,或稱作 branch,如上述例子中的H(H(A) + H(B)) 和 H(H(C) + H(D))。
index:指某個 leaf 所在的位置,leaf = leaves[index],index 如果是偶數,leaf 一定在左邊,如果是奇數 leaf 一定在右邊。
Merkle Proofs
Merkle Proofs 的重點就是要證明資料有沒有在樹上。
如何證明?就是提供要證明的 leaf 以及其相對應的路徑 (path) ,經過計算後一旦能夠產生所需要的 root,就能證明這個 leaf 在這顆樹上。
因此這類要判斷資料有無在樹上的證明,類似的說法有:proving inclusion, proving existence, or proving membership。
這個 proof 的特點在於,我們只提供 leaf 和 path 就可以算出 root,而不需要提供所有的資料 (leaves) 去重新計算整顆 Merkle Tree。這讓我們在驗證資料有沒有在樹上時,不需要花費大量的計算時間,更棒的是,這讓我們只需要儲存 root 就好,而不需要儲存所有的資料。
在區塊鏈上,儲存資料的成本通常很高,也因此 Merkle Tree 的設計往往成為擴容上的重點。
我們知道 n 層的 Merkle Tree 可以存放 2**n 個葉子,以 Tornado Cash 的設計來說,他們設定 Merkle Tree 有 20 層,也就是一顆樹上會有 2**20 = 1048576 個葉子,而我們用一個 root 就代表了這 1048576 筆資料。
接續上段的例子,這顆 20 層的 Merkle Tree 所產生的 Proof ,其路徑 (path) 要從最底下的葉子 hash 幾次才能到達頂端的 root 呢?答案就是跟一棵樹的 levels 一樣,我們要驗證 Proof 所要遞迴的次數就會是 20 次。
在實作之前,我們先來看 MerkleTree 在 client 端是怎麼調用的,這有助於我們理解 Merkle Proofs 在做什麼。
基本上一個 proof 的場景會有兩個人:prover 與 verifier。
在給定一筆 leaves 的樹,必定產生一特定 root。prover 標示他的 leaf 在樹上的 index 等於 2,也就是 leaves[2] == 30,以此來產生一個 proof,這個 proof 的內容大致上會是這個樣子:
對 verifier 來說,他要驗證這個 proof,就是用裡面的 leaf 去一個一個與 pathElements 的值做 hash,上述就是 H('30', 40) 後得出 node,再 hash 一次 H('19786...', node) 於是就能得出這棵樹的 root。
重點來了,這麼做有什麼意義?它的巧思在於對 verifier 來說,他只需要儲存一個 root,由 prover 提交證明給他,經過計算後產生的 root 如果跟 verifier 儲存的 root 一樣,那就證明了 prover 所提供的資料確實存在於這個樹上。
而 verifier 若不透過 proof ,要驗證某個 leaf 是否存在於樹上,也可以把 leaves = [10, 20 ,leaf ,40]整筆資料拿去做 MerkleTree 的演算法跑一趟也能產生特定的 root。
但由 prover 先行計算後所提交的 proof,讓 verifier 不必儲存整批資料,也省去了大量的計算時間,即可做出某資料有無在 Merkle Tree 上的判斷。
Sparse Merkle Tree
上述能夠證明資料有無在樹上的 Merkle Proofs 是屬於標準的 Merkle Tree 的功能。但接下來我們要實作的是稍微不一樣的樹,叫做 Sparse Merkle Tree。
Sparse Merkle Tree 的特色在於除了 proving inclusion 之外,還可以 proving non-inclusion。也就是能夠證明某筆資料不在某個 index,例如 H(A) 不在 index 2 ,這是一般 Merkle Tree 沒辦法做到的。
而要做到 non-membership 的功能其實也不難,就是我們要在沒有資料的葉子裡補上 zero value,或是說 null 值。更多內容請參考:What’s a Sparse Merkle Tree。
實作細節
本節將完整的程式碼分成三個片段來解釋。
首先,這裡使用的 Hash Function 是 MiMC,主要是為了之後在 ZKP 專案上的效率考量,你可以替換成其他較常見的 hash function 例如 node.js 內建 crypto 的 sha256:
crypto.createHash("sha256").update(data.toString()).digest("hex");
這裡定義簡單的 Merkle Tree 介面有 root, proof, and insert。
首先我們必須先給定這顆樹的 levels,也就是樹的高度先決定好,樹所能容納的資料量也因此固定為 2**levels 筆資料,至於要不要有 defaultLeaves 則看創建 Merkle Tree 的 client 自行決定,如果有 defaultLeaves 的話,constructor 就會跑下方一大段計算,對 default 資料開始作 hash 去建立 Merkle Tree。
如果沒有 defaultLeaves,我們的樹也不會是空白的,因為這是顆 Sparse Merkle Tree,這裡使用 zeroValue 作為沒有填上資料的值,zeros 陣列會儲存不同 level 所應該使用的 zero value。假設我們已經填上第 0 筆與第 1 筆資料,要填上第 2 筆資料時,第 2 筆資料就要跟 zeros[0] 做 hash,第 2 筆放左邊, zero value 放右邊。
我們將所有的點不論是 leaf, node, root 都用標籤 (index) 標示,並以 key-value 的形式儲存在 storage 裡面。例如第 0 筆資料會是 0–0,第 1 筆會是 0–1,這兩個 hash 後的節點 (node) 會是 1–0。假設 levels 是 2,1–0 節點就要跟 1–1 節點做 hash,即可產出 root (2–0)。
後半部份的重點在於 proof,先把 proof 和 traverse 看懂,基本上就算是打通任督二脈了,之後有興趣再看 insert 和 update。
sibling 是指要和 current 一起 hashLeftRight 的值…也就是相鄰在兩旁的 leaf (or node)。
到這裡程式碼的部分就結束了。
最後,讓我們回到一開始 client 調用 merkleTree 的例子:
以及 proof 的內容:
前面略過了 proof 裡頭的 pathIndices,pathIndices 告訴你的是當前的 leaf (or node) 是要放在左邊,還是放在右邊,大概是這個樣子:
if (indices == 0) hash(A, B);if (indices == 1) hash(B, A);
有興趣的讀者可以實作 verify function 看看就會知道了!
原始碼
TypeScript from gist
JavaScript from tornado-core
參考
Merkle Proofs Explained
What’s a Sparse Merkle Tree?
延伸:Verkle Tree
Merkle Tree in JavaScript was originally published in Taipei Ethereum Meetup on Medium, where people are continuing the conversation by highlighting and responding to this story.
👏 歡迎轉載分享鼓掌