「我是一名讀大學的男生,我其實很困擾我喜歡男生還女生,性向是可以改嗎?
老實說,我看男生的成人片會有反應,當下也會覺得很興奮,但同時我每次看完後心裡會產生一種強烈的自責和罪惡感,因為我其實很不希望自己是男同性戀,甚至不喜歡我跟男同性戀的標籤沾上邊,我也不知道為甚麼,但可能從小傳統文化給我們的觀念也有關吧!我一直覺得男生喜歡上男生,甚至跟同性做愛是一件很不"正常"的行為,比如說,上天不是就是配對一男一女才有可能發生傳宗接代這件事嗎?那如果男生喜歡男生,他們怎麼生出小孩?我覺得我很在意我腦中的想法,但同時又對男同志的影片著迷⋯⋯
在每次看完男男影片並自慰後的罪惡感,就會讓我覺得很焦慮,想著很糟糕的事,比如說,這封信就是在做完這件事後寫的,我會想要找點東西來補償內心的空虛感和悔恨感,例如:我還會一直看YOUTUBE的影片,想解除內心的空虛,但每次就像滾雪球,罪惡感和自責感就愈來愈大,導致我就浪費了很多寶貴的時間,生活也過得很亂,然後可能有好幾次做完這件事後心情真的很差,我就會暴飲暴食再催吐⋯⋯
我想知道,如何在看完男男成人片並自慰完後解除自己的焦慮與自責並回到原本的生活?」
——風無
有時候我們最大的敵人是自己。
明明理性上面知道某個想法會讓自己產生困擾,但是卻無法停止地會被這個想法給困住(我討厭自己是男同志),同時身體又會做出和想法相違背的反應(可是我喜歡看男男的成人片),於是就會產生認知失調:我腦袋裡面希望我自己成為的人,和我實際上面的渴望與行為不一致。
#認知失調的解套
那該怎麼辦呢?
一般來說,當認知失調出現的時候,有兩個常見的路徑,一個是改變想法(在這個例子當中,是接受自己喜歡男生這個事實),另外一個是改變行為(停止讓自己再看男男的成人影片)這兩條路徑背後的邏輯都是一樣的:讓你的想法跟行為一致。
但有些時候,改變行為非常困難(例如對某件事情已經有慾望、習慣或成癮),改變想法又幾乎不可能(在這個例子當中,就是把「討厭男同志」的想法改成「喜歡男同志」,或是至少不要討厭),這兩條路都行不通,難道沒有第三條的可能嗎?
這次的節目當中提到了一個特別的方法,就是接受兩者都存在。在上面這個例子當中就是:喜歡看男生跟男生的成人片,並且會感到興奮,不一定是男同志。異性戀的男生,甚至是各種不同性別認同的人,都可能因為看男男的成人片而感到興奮。重點是這個影片,是否呈現出情慾的元素,並且勾起你的慾望。
#接受矛盾的存在
還有有一個方法非常好用,就是接受矛盾的存在。在這裡講一個小故事。
前陣子去 Taromak 部落探索 ,Lravorase Lra-madaralrae 在林道裡分享的一段話我覺得很有趣:「你看這個姑婆芋,它其實已經被污名化了。大家都覺得姑婆芋有毒。什麼是有毒呢?就是人吃了之後會中毒、對人類的身體不好。但是這個山裡的姑婆芋,山豬會來吃,尤其是牠腸胃出狀況的時候,這是牠的藥。所以,有些東西是毒也是藥。」
「有些東西是毒也是藥。」最後這段話我老闆講過很多遍了,但我倒是第一次看到姑婆芋本芋,用它的葉子來包食材、做地悶石料理,也是第一次看見上面停放的玉帶鳳蝶蛹(這次運氣很好,分別看到了幼蟲、鳳蝶本蝶來吸食我們烹飪好的地瓜、還有像鐵甲蛹的蝶蛹)。
真正花時間好好端詳姑婆芋,與它學習相處之後,才發現真正可怕的並不是姑婆芋本芋,而是我們腦袋裡面對於姑婆芋的印象。這個方法適用於所有你恐懼的東西——當你凝視惡龍,就會發現惡龍並沒有你想像可怕,可怕的是你的想像。
而接受矛盾的存在是指,你可以同時接受它有毒,也接受它對山豬的腸胃病有幫助。你還可以利用它的葉子包裝食材。就像任何的東西都有不同的面向一樣,你可以接受光明跟黑暗都在同一個人身上存在,不因為你看到黑暗的部分多了,就代表它是不好的。
#接受感覺如實存在
回到這則信箱,在台灣多元性別平權、同志伴侶也合法的今天,很多「恐懼」或者是「害怕」同志的人,還有另外一種更特別的擔心是「覺得自己不應該恐同」,或者是「雖然我各種表現和喜好像同志,但是我希望我的性向可以改變」,這樣的感覺本身並沒有什麼對錯(畢竟很多恐懼是由許多社會文化、過去經驗所累積而成的,很難瞬間調整改變),你只要單純接受這個情緒「是真實的」就可以了。換言之,你可以試著承認自己有一個情緒是:「我看男男的成人片會有罪惡感,我害怕自己是同志,這個感覺是真的」。
像是一雙溫暖的手一樣,把這個感覺捧在手心裡,讓這個感覺不要孤單。
性別的自我認同是一條漫長的路,有時候光是接受自己「和原先的自己想的不一樣」,或者是承認自己「隸屬於一個從來沒有想過的身分」,就要花好幾年、甚至好幾十年,走一趟認同的旅程。此外,有些時候你「好不容易接受的認同」,會隨著時間有所改變。
就像有些人一開始認同自己是異性戀,後來發現自己喜歡同性別的比較多,漸漸認同自己為同性戀,甚至後來發現自己在性取向上面有更多複雜多元的狀況,於是開始跳脫出兩個向度光譜的性別認同框架,採無性戀或者是其他性別認同,所所以才會說這是一條漫長的路。
矛盾本來就是人生的日常。
或許終其一生重要的並不是你喜歡男生或者是女生,而是你有沒有辦法成為自己本身。當你真實地先接納自己的情緒,你也會慢慢地成為自己。
p.s.對性別認同我了解也有限, 更多資源可以到 同志諮詢熱線 詢問,也歡迎大家留言補充。
#今天的節目這裡聽
https://bit.ly/3i2aGNi #海苔熊心理話 #同志 #自我認同 #性別認同 #海苔熊信箱
同時也有4部Youtube影片,追蹤數超過0的網紅YorkPau,也在其Youtube影片中提到,鴨洲位於香港的東北部,沙頭角海以東,吉澳以西,是一個有人居住而面積最小的島嶼,全島面積只有0.016平方公里,島的形狀好像一隻半浮在水面的鴨子,因而命名為「鴨洲」!鴨洲擁有香港罕見的褐紅色角礫岩(沉積岩的一種)和獨特的地質環境,被納入香港聯合國教科文組織世界地質公園範圍內,島的北部(鴨頭位置)有個海...
最後一片葉子學習單 在 Taipei Ethereum Meetup Facebook 的最佳貼文
📜 [專欄新文章] Tornado Cash 實例解析
✍️ Johnson
📥 歡迎投稿: https://medium.com/taipei-ethereum-meetup #徵技術分享文 #使用心得 #教學文 #medium
Tornado Cash 是一個使用 zk-SNARKs 建立的 Dapp,它實現了匿名的代幣交易,這篇文章就用一些程式碼片段,來分享它是怎麼運作的。
本文為 Tornado Cash 研究系列的 Part 3,本系列以 tornado-core 為教材,學習開發 ZKP 的應用,另兩篇為:
Part 1:Merkle Tree in JavaScript
Part 2:ZKP 與智能合約的開發入門
Special thanks to C.C. Liang for review and enlightenment.
我們知道在以太坊上的交易紀錄都是公開的,你可以在 etherscan 上看到某個地址的所有歷史交易紀錄,當然地址是合約的話也是一樣。
也許創建一個新的錢包和地址就好了?假設一個情境是 Alice 想要匿名傳送 1 ETH 給 Bob,Alice 原本的錢包是 A,但她不想讓 A 地址傳給 Bob 的交易紀錄被看到,所以 Alice 創建另一個錢包 B,顯然 B 錢包是空的,Alice 必須把 A 錢包的 1 ETH 傳到 B 錢包,再用 B 錢包的地址傳給 Bob。
但問題就在於,只要追蹤 B 錢包的地址,就能看到 B 的歷史交易紀錄中 A 錢包曾經打幣給 B 錢包,於是到頭來交易還是被追蹤到了。
Tornado Cash 的解決方案,簡單來說,它是一份合約,當你要匿名傳送代幣時,就把一定數量的幣丟進合約裡 (Deposit),此時你會拿到一個 note,長得像這樣:
tornado-eth-0.1-5-0x3863c2e16abc85d72b64d78c68fca5936db2501832e26345226efdfb2bc45804977f167d86b711bb6b4095ddaa646ec93f0a93ac4884a66c1d881f4fc985
note 就是一串字串,擁有這字串的人,就能提領 (Withdraw) 剛剛傳入合約的代幣。握有 note 就代表擁有提款的權利,所以 note 一旦被別人知道,別人就可以把錢給提走。
其中,後面那段亂碼,本篇文章就以「秘密」來稱呼,這個秘密是由 secret 與 nullifier 組成,而這兩個都是在鏈下隨機產生的亂數。
因此 Tornado 的合約基本上會有兩個函式:
Deposit
Withdraw
有興趣的人可以先到 Dapp 上先玩一次看看,使用 Goerli 測試網,這裡可以領 Goerli 的代幣:https://goerli-faucet.slock.it/
Deposit
我們就從 Deposit 開始說起,簡單來說, Deposit 是將資料儲存到合約的 Merkle Tree 上。
剛剛提到的秘密,它是在鏈下產生,由 secret 跟 nullifier 組成,合在一起之後也稱作 preimage,因為我們要對這個 preimage 進行 hash,就會成為 commitment。
合約中 Deposit 如下:
deposit 除了傳送代幣到合約之外,需填入一個參數 _commitment。
我們對 preimage 使用 Pedersen 作為 hash function 加密後產生 commitment,以偽代碼表示如下:
const preimage = secret + nullifier;const commitment = pedersenHash(preimage);
這個 commitment 會成為 Merkle Tree 的葉子,所以合約中的 _insert(commitment) 來自 MerkleTreeWithHistory.sol 的合約,將我們的資料插入 Merkle Tree,然後回傳一個 index 給你,告訴你這個 commitment 在 Merkle Tree 上的位置,最後一起發布成公開的 Deposit 事件。
我們知道 MerkleTree 是將一大筆資料兩兩做雜湊後產生一個唯一值 root,這個 root 就是合約上所儲存的歷史資料。
root 的特性就是只要底下的資料一有更動,就會重新產生新的 root。
所以只要一有用戶 deposit ,就會插入新的葉子到 Merkle Tree 上,於是就會產生新的 root,所以在合約中有一個陣列是用來儲存所有的 root 的 roots:
bytes32[ROOT_HISTORY_SIZE] public roots;
roots 是用來紀錄每個 deposit 的歷史,每一次 deposit 都會創造新的 root,而所有 root 都會被儲存進 roots 裡,於是當你要提領的時候,就要證明你的 commitment 所算出的 root 曾經出現在 roots 裡,代表曾經有 deposit 的動作,因此才可以進行提領。
Withdraw
在 Deposit 之前 Tornado Cash 就會在鏈下產生秘密後交給使用者,擁有這個秘密的人等於擁有提款的權利。
提領的時候,秘密會在鏈下計算後產生 proof,proof 是 withdraw 需要的參數,所以只要確保這個 proof 能夠被驗證,那麼代幣的接收地址 (recipient) 就可以隨便我們填,只要不填上當初拿來 deposit 用的地址,基本上就做到匿名交易的效果了。
也就是說,產生這個 proof 並提交給合約,能夠證明此人知道秘密,但卻不告訴合約秘密本身是什麼。
function withdraw(bytes calldata _proof, bytes32 _root, bytes32 _nullifierHash, address payable _recipient, address payable _relayer, uint256 _fee, uint256 _refund) external payable nonReentrant;
我們可以清楚看到 withdraw 函式裡沒有接收有關秘密的任何資訊作為參數,也就是秘密不會與合約有所接觸,也不會暴露在 etherscan 上。
回顧 ZKP 所帶來的效果:
鏈下計算
隱藏秘密
在 Tornado Cash 的例子中,我們用秘密來產生證明,完成的鏈下計算包括:
將秘密 hash 成 commitment
算出 Merkle Tree 的 root。
以下是簡化後的 withdraw.circom:
template Withdraw(levels) { signal input root; signal input nullifierHash;
signal private input nullifier; signal private input secret; signal private input pathElements[levels]; signal private input pathIndices[levels];
component hasher = CommitmentHasher(); // Pedersen hasher.nullifier <== nullifier; hasher.secret <== secret; hasher.nullifierHash === nullifierHash;
component tree = MerkleTreeChecker(levels); // MiMC tree.leaf <== hasher.commitment; tree.root <== root; for (var i = 0; i < levels; i++) { tree.pathElements[i] <== pathElements[i]; tree.pathIndices[i] <== pathIndices[i]; }}
component main = Withdraw(20);
從上述代碼就可以看出這份 circuit 的 private 變數有:
secret
nullifier
pathElements
pathIndices
而 public 變數有:
root
nullifierHash
如同我們一開始說過的,秘密就是指 secret 與 nullifier。這裡進行的鏈下計算就是對 secret 與 nullifier 雜湊成 commitment。而使用的 hash function 叫做 Pedersen。
在進行 Merkle Tree 的計算之前,我們還檢查了 nullifier 雜湊後的 nullifierHash 跟 public 變數 nullifierHash 是不是一樣的。
hasher.nullifierHash === nullifierHash;
接下來,開始計算 Merkle Proof,用意是確認經過雜湊後的 commitment 有沒有出現在 Merkle Tree 上,所以我們的 private input 還有 pathElements 與 pathIndices(詳情參考 Part 1 Merkle Tree in JavaScript),讓它跑一趟 Merkle Proof 的計算,最後就能夠算出一個 root,再確認計算後的 root 與我們的 public 變數 root 是否一樣。
tree.root <== root;
於是我們就能產生一個 ZKP 的證明 — 證明 private 變數:secret, nullifier, pathElements, pathIndices 可以計算出 public 變數:root 與 nullifierHash。
把這個證明提交給合約,合約透過 Verifier 驗證 proof 是否正確,以及必須事先確認:
public 變數 root 有在合約的 roots 裡面。
public 變數 nullifierHash 在合約中是第一次出現。
以下附上完整的 withdraw 原始碼:
必須注意 ZKP 是向合約證明使用者填入的 secret 和 nullifier 可以計算出某個 root,但無法保證這個 root 曾經在合約的 roots 歷史上。
所以合約的 withdraw 中,除了 verifyProof 之外,還要事先檢查 ZKP 算出來的 root 是不是真的在歷史上發生過,所以需要 isKnownRoot 的檢查:
function isKnownRoot(bytes32 _root) public view returns(bool)
必須先檢查 isKnownRoot 後才能進行 verifyProof。
經過 verifyProof 驗證成功後,合約就開始進行提款的動作,也就會將代幣傳到 recipient 的地址,最後拋出 Withdrawal 的事件。
nullifier 與 nullifierHash
為什麼我們的秘密不是只有 secret 還要額外加一個 nullifier?
簡單來說,這是為了防止已經提領過的 note 又再提領一次,也就是所謂的 double spend。
require(!nullifierHashes[_nullifierHash], "The note has been already spent");
可以看到 withdraw 需要填入參數 nullifierHash,跟 isKnownRoot 一樣的狀況,我們需要對電路的 public 變數先經過一層檢查之後,才能帶入到 verifyProof 裡面。
nullifierHash 可以理解為這個 note 的 id,但它不會連結到 deposit,因此可以用來紀錄這個 note 是否已經被提領過。
所以當 verifyProof 驗證成功之後,我們要紀錄 nullifierHash 已完成提領:
nullifierHashes[_nullifierHash] = true;
有關為什麼需要事先檢查 public 變數後,才能帶入 verifyProof ,可以參考 Part 2:ZKP 與智能合約的開發入門 提到的 publicSignals 的部分。
附上 Tornado Cash 的架構圖:
簡化版的 tornado-core
tornado-core 的程式碼很簡潔漂亮,所以我模仿該專案自己實作一遍:
simple-tornado:https://github.com/chnejohnson/simple-tornado
這份專案只完成了 tornado-core 的核心部分,不一樣的是我的開發環境使用 hardhat 與 ethers 寫成,而 circom 與 snarkjs 使用官方當前的版本,合約用 0.7.0,測試使用 Typescript 。
比起兩年前的 tornado-core ,simple-tornado 使用的技術更新,可能更適合初學者理解這份專案,但是它有 bug…我在 issues 的地方有紀錄說明。
在開發的過程中,我的順序是先從最小單位的 MiMC hash function 開始玩,發現必須 javascript 算一次 hash、solidity 算一次、circom 再算一次,確保這三個語言對同一個值算出同樣的 hash 之後,才能放心去做更複雜的 Merkle Tree。
總結
我們可以看到 Tornado Cash 簡單的兩個函式:Deposit 與 Withdraw,透過將代幣送入合約後再提領到另一個地址的流程,應用 ZKP 達成匿名的交易。
除了斷開 Deposit 與 Withdraw 的地址關聯性之外,Tornado Cash 還有做了一層「藏樹於林」的隱私防護,這部份的解釋就請參考 ZKP 讀書會 Tornado Cash。
網路上很多關於 ZKP 的文章或專案都是在 2019 年後出產的,經過許多人對這項技術的嘗試,讓我們對 ZKP 有了更清晰的理解,如今兩年後,開發工具也變得更加成熟,期待未來在 web 隱私議題上能看到更多 ZKP 大放異彩的應用。
原始碼
tornado-core
simple-tornado
參考資料
ZKP 讀書會 Tornado Cash
Tornado Privacy Solution Cryptographic Review
Tornado Cash 實例解析 was originally published in Taipei Ethereum Meetup on Medium, where people are continuing the conversation by highlighting and responding to this story.
👏 歡迎轉載分享鼓掌
最後一片葉子學習單 在 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.
👏 歡迎轉載分享鼓掌
最後一片葉子學習單 在 YorkPau Youtube 的最佳解答
鴨洲位於香港的東北部,沙頭角海以東,吉澳以西,是一個有人居住而面積最小的島嶼,全島面積只有0.016平方公里,島的形狀好像一隻半浮在水面的鴨子,因而命名為「鴨洲」!鴨洲擁有香港罕見的褐紅色角礫岩(沉積岩的一種)和獨特的地質環境,被納入香港聯合國教科文組織世界地質公園範圍內,島的北部(鴨頭位置)有個海蝕拱,形成了「鴨眼」,是非常受歡迎的地質景點,這也是吸引我們來鴨洲的原因呢!
吉澳與鴨洲相差大約15分鐘的船程,位於大鵬灣以西、沙頭角以東,船灣淡水湖以北,是香港最接近中國大陸的離島,全島面積2.35平方公里,島的形狀有點像相反的英文字母「Z」,彎曲的海岸線可作為天然避風港,漁民視為吉祥的海灣,因而得名「吉澳」! 1950年代,吉澳曾是水陸居民聚居的熱鬧島嶼,人口達四千餘人,多以捕魚、耕種及經營商店維生。後期居民陸續移民歐洲和澳洲等國家,現在只剩下大約不到50人居住!來列吉澳,一定要去參觀「吉澳故事館」了解當地的文化歷史;到訪百年姻緣樹;登上觀景台欣賞印洲塘面貌;最後記得要品嚐吉澳正宗手打墨魚丸和客家菜呢!
前往鴨洲和吉澳的交通和路線:
起點:在馬料水的三號梯台乘搭來往馬料水至吉澳與鴨洲的渡輪,船程大約2小時!
船票:來回票$90 (吉澳往馬料水單程票$50)
注意:
-渡輪服務是逢星期六、星期日和公眾假期行駛
-只收現金
船上有導賞員沿途講解
由2020年12月開始至2021年11月期間,實行「R2G地質公園導賞員街渡公眾導賞計劃」,吉澳/鴨洲街渡航線會有R2G地質公園導賞員在船上沿途講解!
有關馬料水至吉澳鴨洲航線時間表:
由馬料水開往吉澳:上午 09:00
到達吉澳停留一陣:上午 10:30(不去鴨洲只去吉澳的乘客可以在這裡上岸)
由吉澳開往鴨洲:上午 10:45
到達鴨洲:上午 11:00(上岸遊覽鴨洲大約1.5小時)
由鴨洲開往吉澳:下午12:30
由吉澳返回馬料水:下午 15:30
到達馬料水:下午17:00
注意:每一站開出時間必需準時上船, 逾時不侯!
預早購票及查詢:可致電2555 9269 / Whatsapp 5540 0002
回程:從馬料水三號梯台下船,步行大約15分鐘至地鐵東鐵線的大學站!
???遊記分享:
https://yorkyorkpau.blogspot.com/2021/04/1.html
#鴨洲 #吉澳 #香港假日離島好去處
___________________________________________________________________
其他??香港遊影片:
【香港行山好去處】大欖涌水塘。千島湖清景台|屯門出發。回程黃金海岸看日落|懶人行山。最短最快最輕鬆的路線(步行大約20分鐘已經見到大欖涌水塘)
https://youtu.be/pQi8GubP43g
【香港好去處】錦上路。錦田半日遊|大水管。有機農莊。壁畫村|隱世泰國餐廳推介。彷如置身泰國度假
https://youtu.be/Ga0FSfeCNic
【香港好去處】九龍公園?鳥湖|嘩!好多隻Flamingoes ?呀!!!
https://youtu.be/jbcdwW_2OBw
【香港好去處】荃灣光板田村。彩虹樓梯。壁畫|尋找隱世「港版彩虹眷村」(含清晰的步行路線來回不用1小時)
https://youtu.be/4xIJzXyUIBM
【香港離島好去處】坪洲輕鬆行山路線|手指山。東灣。釣魚翁石。大利島|踏上坪洲最高點|飽覽超過180度的海景
https://youtu.be/GPLOfYxC-xo
【香港郊遊靚景好去處】欣澳>鹿頸村>長索|輕鬆超易行路線|浮木。水上木屋。連島沙洲。日落。晚霞
https://youtu.be/hLLkAvItRtc
【香港行山好去處】良田坳峽谷。下白泥日落|路痴行山新手話你知?係咪真係好易行?|靚景行山路線分享
https://youtu.be/2_4Qx_ImLEY
【香港好去處】烏蛟騰>三椏村|紅葉。竹林。小溪澗。紅樹林。海岸。鄉村|靚景行山路線分享
https://youtu.be/J3CmF5Dl6Kc
青衣公園紅葉?落羽杉|金黃色的葉子?開始變紅了|最輕鬆的欣賞紅葉路線?
https://youtu.be/Eqz73djNm5M
青衣三枝香|海景。山景。日落景。夜景|靚景行山路線分享
https://youtu.be/37hle65XDBA
香港仔漁港體驗|浪遊漁港1773|乘坐舢舨船+參觀住家艇+品嚐正宗傳統艇仔粉
https://youtu.be/PFdUAc9OOHA
漫步圍村:元朗錦田吉慶圍
https://youtu.be/vqQvyezzXB4
___________________________________________________________________
?歡迎大家訂閱我嘅YouTube頻道:
https://www.youtube.com/c/YorkPau
Update 住我嘅影片喇!
更多的分享與交流:
lnstagram:https://www.instagram.com/york.york.freestyle_traveler/
Facebook:https://www.facebook.com/lifegoesonfreestyletraveler/
Blog: http://yorkyorkpau.blogspot.com/
![post-title](https://i.ytimg.com/vi/k8hKWGA70FM/hqdefault.jpg)
最後一片葉子學習單 在 李蘋果 Youtube 的最佳貼文
李蘋果首部MV【簡單生活】HD
【簡單生活】曲目介紹:
期待能透過青春洋溢的旋律,將充滿歡樂與活力的正面能量傳達給大家,希望那些在愛錯裡獨自傷心難過的人,能夠放下執著,甩開寂寞、找回笑容,簡簡單單開心的生活!
—————————————-———
【簡單生活 】
葉子在窗外輕輕擺動
冰涼的夜裡 讓眼淚它溫熱我
鏡子裡的我很不像我
你離開了以後 我變得好軟弱
甩開了寂寞 我大步向前走
一無所有反而輕鬆
找個人愛了 到底有沒有錯
我只要過簡單的生活
丟掉了執著 我要找回笑容
偶爾放肆一下真快活
隨便人去說 別再囉哩叭唆
我只要過簡單的生活
你的影子在每個角落
像提醒著我 沒有你我多寂寞
愛情怎麼是這種結果
你離開了以後 我變得好脆弱
甩開了寂寞 我大步向前走
一無所有反而輕鬆
找個人愛了 到底有沒有錯
我只要過簡單的生活
丟掉了執著 我要找回笑容
偶爾放肆一下真快活
隨便人去說 別再囉哩叭唆
我只要過簡單的生活
只要過簡單的生活
簡單的生活
只要過簡單的生活
簡單的生活
只要過簡單的生活
簡單的生活
只要過簡單的生活
啦啦啦啦啦啦啦啦
丟掉了執著 我要找回笑容
偶爾放肆一下真快活
隨便人去說 別再囉哩叭唆
我只要過簡單的生活
—————————————-———
數位平台收聽🎵
KKBOX→https://www.kkbox.com/tw/tc/album/rl56-YKsLkDt10F54eK3009H-index.html
Spotify→ https://open.spotify.com/album/0TI4QMjvX4iJXlm9627LnA?si=NuxyOOFPRYuwgeG_2TFZsA
friDay音樂→https://reurl.cc/5qlKQn
MyMusic→ https://reurl.cc/avreD3
蝦米音樂→https://reurl.cc/0OoKrY
QQ音樂→ https://reurl.cc/0OoK76
酷狗音樂→ https://reurl.cc/Y613WX
酷我音樂→ https://reurl.cc/Ez7Q2v
—————————————-———
製作人| 李蘋果
詞曲| 李蘋果
編曲| 姜道
錄音| 姜道
合聲編寫| 姜道
混音| 姜道
—————————————-———
MV友情客串
吉他手|武靖
貝斯| 阿京
鼓手| 小芳
完美工具人| 小木子
貓貓| 圓圓、萌萌
—————————————-———
MV製作團隊
Foolkids Film Studio
導演 | 呂小猴
製片 | 鄭城庭
攝影 | 張永日
攝影助理 | 廖柏竣
燈光 | 梁敦學、廖景棟
美術 | 林宗賢
美術助理 | Anna Huang
剪輯調色 | 鄭閎仁
妝髮|許家綺 Bonnie Hsu
妝髮助理|林慧雯
—————————————-———
【關於】
🍎李蘋果:詞/曲+Vocal+Guitar
一個個子嬌小,卻對音樂懷抱著熱情的女孩。從小學習古典鋼琴,2009年開始嘗試歌曲創作,自學吉他以及編曲,至今已累積百首以上作品!
2011年接觸各類表演工作,2012年取得街頭藝人證照,個人演出次數超過百場,翻唱過百首以上流行歌曲!
2013年獨立發行首張迷你創作專輯『嚇一跳』
關於愛情、還有生活的美好與失落,歡笑及感動。每段故事裡,訴說著她想傳遞給大家的各種心情。
像沉溺在陽光溫暖的午後,那麼放鬆自在;像陶醉在戀人的懷抱,幻想著去環遊世界!微笑哼著歌曲,拋開生活中的煩惱和壓力,只要聽她唱歌,就是那麼容易感覺到幸福!喜歡大聲笑鬧,自然清新的曲風,搭配著甜美的歌聲,李蘋果和她的吉他,要唱出讓你”嚇一跳"的音樂!
2017年獨立發行第二張數位創作專輯『睡美人』
睽違四年,見證李蘋果的蛻變,從事音樂創作與街頭演出,累積不少歌迷的李蘋果,在2013年發行第一張專輯後因聲帶萎縮,找不到人生方向,一度萌生放棄音樂的念頭,期間更罹患躁鬱症。在製作人、家人、粉絲們的鼓勵,與出國散心累積創作能量,將走過人生谷底的經歷譜成音符,用歌聲詮釋更成熟的的人生觀。
2020年獨立發行第三張數位創作EP 『簡單生活』
創作音樂至今已經十多年的李蘋果,為了發行第三張創作EP,她拍了生平的第一部MV。為了籌備這部MV的資金和製作宣傳,她付出相當大的努力!因遭逢疫情,演出和收入也跟著受到影響。為了讓更多人注意到她的音樂和創作,她開始嘗試轉型,沒想到竟意外的增加她的曝光度及點閱率!粉絲們看到她轉型,誤會她已經放棄音樂,其實她會轉型也是為了音樂。因為沒有在做音樂的人,很難體會做音樂有多燒錢,加上現在音樂數位化,會去買實體唱片的人是寮寮可數;想靠音樂賺錢生活更是難上加難!甚至去向銀行貸款做專輯。這十多年來,李蘋果沒有唱片公司、經紀公司,全都是自掏腰包。這條路上跌跌撞撞,也曾有過好幾次想放棄的念頭⋯但她還擁有最後一絲的夢想,還想繼續走下去。為了音樂她會繼續努力,也希望大家可以多多支持李蘋果的副業,讓她能繼續創作更多好聽的音樂給大家聽!
—————————————-———
【表演傳記】
河岸留言、女巫店、覺響Live house、樹樂集
台中迴響、台南Room335、台東鐵花村、三峽甘樂文創
SNAPPP、A House、阪急音樂花房、7-11門市開幕演唱
D.Cafe 、Trip Cafe、Somebody Cafe、口袋咖啡、桃園There Cafe
信義誠品店、天母店、台中店、台南店、高雄店等文藝空間或咖啡店演出
【駐唱經歷】
公館PIPE水岸廣場、131 pub
漁人碼頭、紗帽古溫泉音樂餐廳、好客燒烤餐廳、碧潭水灣、淡水榕堤、巴塞隆納
內湖737巷夜市、桃園御花園夜市、八德興仁夜市、西門町、信義區等街頭表演
【電視經歷】
2006年8月我猜我猜我猜猜猜-網路美女
2010年3月國光幫幫忙-音樂美女
2011年11月鑽石夜總會-吉他彈唱大賽
2012年7月超級偶像七-前20強
2013年12月中天、東森新聞採訪報導
2014年7月我要當歌手-3集
【電台經歷】
警廣-播音747專訪
飛碟-阿宅的反抗軍
News98-愛你22小時
教育電台-麻辣學堂
【發表作品】
寶島鐘錶55週年廣告曲
超視偶像劇『33故事館』插曲
警察廣播電台台呼製作-入圍電台廣播金鐘獎
2013年3月獨立發行首張迷你創作專輯『嚇一跳』
2017年獨立發行第二張數位創作專輯『睡美人』
2020年獨立發行第三張數位創作EP 『簡單生活』
【活動代言】
2013年代言網路遊戲『劍俠Q傳』,創作兩首遊戲主題曲,網聚活動自彈自唱演出
—————————————-———
🍎李蘋果🍎
YouTube➤ https://reurl.cc/0OgOlM
Facebook➤ https://reurl.cc/r8d89r
Instagram➤ https://reurl.cc/GrZrpZ
Twitter➤ https://reurl.cc/av0veD
街聲Street Voice➤ https://reurl.cc/Ld13ba
蝦皮➤ https://reurl.cc/8n4n8M
🔎演出、教學、婚禮、商演、駐唱、活動邀約歡迎來信:mrsapple7763@gmail.com
![post-title](https://i.ytimg.com/vi/CS481GegzxU/hqdefault.jpg)
最後一片葉子學習單 在 李基銘漢聲廣播電台-節目主持人-影音頻道 Youtube 的最佳解答
本集主題:「心中住了一隻貓:我們和貓一起的日子」介紹
訪問作者:葉子
內容簡介:
你的心中是不是住著一隻貓?
用貓的態度過日子,即使忙碌也要步調從容;
用貓的哲學處世,不迎合外界世俗;
用貓的好惡分明,沒得商量也絕不妥協。
貓住進家裡,住進心裡,也住進一起生活的日子裡。
曾經是看到黑貓就覺得會倒楣一整天的人,二十多年前從路邊抱起一隻眼睛爆膿的小貓,從此牽起與貓咪的不解之緣。
葉子和先生KT擔任貓中途公寓終身管家近二十年,散盡掙來的每一分錢為貓治病、給貓足食,最後為了貓舉家搬遷。兩個人,三十六隻貓在花蓮縱谷間一起建立了「貓房子」,一個貓咪永遠的家。
她說:「如果貓中途是個照顧中途貓的開端,我想事情一定得有結尾,現在我們做的,就是最後的照顧,一個穩定、安心的終途照顧。」
就這樣,人把家與貓共享,與其說貓需要人照顧,不如說是人依賴貓的存在。葉子在書中寫到:「我們給予貓物質上的需求,貓給予我們精神上的歸依,一個有貓的房子,即使貓安安靜靜躺著睡覺,都能讓人覺得自己並不孤單。」在她心裡,沒有貓的房子稱不上家,一個有貓的房子,就是最接近天堂的地方。
她寫下貓房子裡的點點滴滴,從自己如何由養狗人變愛貓人,如何邂逅生命裡的每一隻貓,再為了貓買下一個家,費盡心機翻山越嶺帶著三十七隻貓搬家。因為心中住了一隻貓,所以心甘情願放棄最愛的旅行和電影,為初生奶貓餵奶驅尿、為重傷成貓照護復健、為老貓灌食維生。時間碎切成片,日子反覆在整理、餵飯、打皮下、看醫生、灌食,還有陪伴下度過,如果不是真愛,如何能周而復始做著同樣的事情?
這一切都從那個電光火石的片刻開始:當人遇見貓,生活幡然改變,生命也重新定義。家,從一隻貓開始,從愛自己的貓開始,分享愛給其他需要救助的貓,再幫助這些貓找到另外一個家。作者分享她擔任貓中途管家的經驗,記錄每一次在街頭與貓的巧遇,私語每一個愛貓成癡的貓族人,更向陪伴了二十年的老貓溫馨告別。她期許這本書可以變成一種善的流動,讓所有人的家變成一個能幫助更多貓,更有意義的家。
作者簡介:葉子
葉子,網路上使用名稱為Leaf Yang,二十幾年前意外救助一隻雙眼爆膿的小貓開始,與貓的緣分從原本的平行線交織成密不可分的感情線。先前無償擔任貓中途,救助流浪貓無數,並在照顧送養貓咪的過程中學習到的經驗透過書寫,把照顧餵奶貓幼貓懷孕貓等經驗分享給更多人知道。
現則終養三十六隻米克斯貓,一半以上都是超過十幾歲以上的老貓,與先生定居後山縱谷間,並為貓建立一個專屬的貓房子。
著有:《貓中途公寓三之一號》
出版社粉絲頁: 四塊玉文創/橘子文化/食為天文創 三友圖書-微胖男女編輯社
請大家支持,我全部六個粉絲頁
李基銘主持人粉絲頁:https://www.facebook.com/voh.lee
李基銘的亂亂分享粉絲頁:https://www.facebook.com/voh.happy
李基銘的影音頻道粉絲頁:https://www.facebook.com/voh.video
漢聲廣播電台「fb新鮮事」節目粉絲頁:https://www.facebook.com/voh.vhbn
漢聲廣播電台「快樂玩童軍」節目粉絲頁:https://www.facebook.com/voh.scout
漢聲廣播電台「生活有意思」節目粉絲頁:https://www.facebook.com/voh.life
![post-title](https://i.ytimg.com/vi/22yBXjssFyU/hqdefault.jpg)