📜 [專欄新文章] 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.
👏 歡迎轉載分享鼓掌
「流程圖template」的推薦目錄:
- 關於流程圖template 在 Taipei Ethereum Meetup Facebook 的最讚貼文
- 關於流程圖template 在 微笑。倫敦。日不落 【Keep Smiling and Carry On】 Facebook 的最讚貼文
- 關於流程圖template 在 施振榮 Stan哥 Facebook 的最佳貼文
- 關於流程圖template 在 PowerPoint『PPT免費簡報素材』魚骨圖、流程圖、動畫特效等 ... 的評價
- 關於流程圖template 在 流程圖範例下載2023-在Facebook/IG/Youtube上的焦點新聞和 ... 的評價
- 關於流程圖template 在 流程圖範例下載2023-在Facebook/IG/Youtube上的焦點新聞和 ... 的評價
- 關於流程圖template 在 如何繪製美觀的組織圖+ 人物關係圖? | PowerPoint 教學#10 的評價
- 關於流程圖template 在 2023 寶可夢let s go 鎖 - herseyezamm.online 的評價
流程圖template 在 微笑。倫敦。日不落 【Keep Smiling and Carry On】 Facebook 的最讚貼文
【新家裝修ep4:廚房上集】
原本在看房的時候,廚房看起來感覺是不用裝修的,入住了之後,才發現很多櫃體都壞了,還伴著油垢和隔夜便當的味道,每次走進廚房,待久一點就會像害喜一樣作嘔⋯
再加上我們家是重度使用廚房的家庭,所以我馬上決定要重新裝修廚房,整個砍掉重練⋯
(我有些媽友的廚房就是裝飾成份多於使用XD是在說誰呢?)
英國比較耳熟能詳的廚房裝修/設計公司,大概如下:
Wren Kitchen➡️ www.wrenkitchens.com
Diy-kitchen➡️ diy-kitchens.com
Howdens➡️ www.howdens.com/kitchens
Magnet➡️ www.magnet.co.uk
Wickes➡️ www.wickes.co.uk/kitchen
我們在去年12月底因為蘿蔔得到COVID-19全家隔離期間,約了好多間線上諮詢,第一間就是媽友推薦給我的Wren Kitchen,然後諮詢了第一間後就覺得是他了,後面陸陸續續又進行了1-2間,都覺得沒有第一間好⋯
怎麼說呢?
我覺得從線上諮詢就可以感覺設計師的專業度,第一次諮詢我弱弱地提供了我家廚房「大概」的長寬高,結果設計師三兩下馬上畫出3D圖出來,把所有櫃子都排好,我下巴都快掉下來⋯
還有容不容易找到人,我們的設計師根本就是隨傳隨回,有什麼問題馬上whatsapp,她就會馬上回答⋯
再來就是,我們在每個階段都沒有延誤到,而且如果發現櫃體有些瑕疵,他們會馬上寄新的來!
《大致流程》
1⃣️線上諮詢階段 2020.12.26
上面說了,只要提供給設計師妳家廚房大概尺寸、想要的色調、櫃面材質(光面Gloss還是霧面Matt)、風格(現代、鄉村、義式⋯)⋯他們就會線上畫圖給妳看,套上各種櫃子供選擇⋯
我們的廚房很簡單,我想要現代感,黑灰白色為主,大項地選擇櫃子、選擇工作檯面(流理台)、選擇電器,就差不多了⋯
我們前前後後大概進行了2-3次的線上諮詢,還有無數個訊息來來回回討論細節⋯因為疫情關係,他們的show room是關閉的,所以無法親眼看親手摸⋯但因為他們視覺化做得極好,我覺得不親臨現場好像也無所謂⋯
初步確認後,會先付5-10%的訂金!
wall cabinet 上櫃(裝在牆壁的吊櫃)
base cabinet 下櫃
tower cabinet 頂天立地的櫃子
appliance cabinet 電器櫃
2⃣️ Order Placed下單 2021.01.08
3⃣️Showroom Appointment親臨展示間看(因疫情取消)
4⃣️ Home Measure現場測量 2021.01.13
廚房公司派專人到妳家廚房丈量,量得非常仔細的那種,窗戶的位置、插座的位置、各種電器的位置⋯整個進行時間在1-2小時左右!
5⃣️Confirm order & contract 確認訂單+簽約 2021.02.12
6⃣️Delivery 廚房派送 2021.03.03
我們廚房很準時的在說好的3/3一大清早7am送到,我和蘿蔔絲興奮地在樓上房間看卸貨!我們選的是fully build(櫃子已組裝好)不是flat pack(ikea那種來木版和螺絲自己組裝),所以佔滿了整個客廳!
7⃣️Cabinet Installation安裝櫥櫃 2020.03.02-06
8⃣️Worktop Template丈量工作檯(流理台)2021.03.08
這個步驟必須在安裝完櫃子之後,廚房公司會派專業的人來量,確認後才會切割工作檯,我們選的是石英材質quartz!
9⃣️Worktop Installation安裝工作檯(流理台)2021.03.17
🔟After Care售後服務
Wren Kitchens
上集先到這,未完待續
.
.
.
.
【新家裝修ep4:廚房上集】
https://www.facebook.com/538009149697002/posts/1922504101247493/?d=n
【新家裝修ep3:碎碎念篇】
https://www.facebook.com/538009149697002/posts/1915931411904762/?d=n
【新家裝修ep2:IKEA篇】
https://www.facebook.com/538009149697002/posts/1882412175256686/?d=n
【新家裝修ep1:先搞定地板】
https://www.facebook.com/538009149697002/posts/1875506985947205/?d=n
流程圖template 在 施振榮 Stan哥 Facebook 的最佳貼文
台灣不缺人才,缺舞台!施振榮:最怕年輕人眼裡只看的見中美日韓
文— 張道宜 . Cheers雜誌 2020-11-23
從「宏碁創辦人」、「品牌教父」、「Stan哥」,75歲的施振榮在科技業戰功彪炳,但許多人不知道,他還是一位攝影好手。
拿起逾50年前施媽媽送給他的單眼相機,不只架勢十足,解釋起攝影原理毫不生疏,還反問記者,「你聽得懂嗎?」
這部寫滿時光刻痕的相機,對施振榮別具意義,因為是他年輕時建立自信的開端。當年進入交通大學後,他謙稱自己是重考生,「比較油條,願意為大家服務」。
從喜歡攝影開始,施振榮一口氣創了攝影社、桌球隊、排球隊與棋橋社,無形間也成為他養成領導特質的開端。
「我說的領導,不是上對下的指揮,而是平輩領導。大家都是同學,誰要聽你的?」施振榮解釋。
特別在球隊,場下是隊長,要整合全隊;在場上也是球員,要並肩作戰,以勝利為最終目標,「這個時候,隊長的責任除了策略,還要提振大家的信心。」
「策略與信心」,同樣是現在施振榮試圖帶給宏碁集團的價值。雖然宏碁在2014年進入執行長領軍的「陳俊聖時代」,但施振榮在轉型過程中,仍扮演輔助新事業成長的角色。
一直到今年,他每個星期還是會花一天時間與公司30歲上下、負責各項專案的經理或副理開會,確認計畫進度。
「Acer作為一個後來者,跟著人走,一定沒門。我現在看到一個窄門,就要慢慢擴大,這是我定義的策略,」
施振榮指出,例如國外做公有雲,宏碁就推出私有雲BYOC;國外做雲端AI,宏碁則從邊緣運算AI切入:
「要說服大家這裡有路、有機會,才能讓年輕一輩願意付出時間與青春,在這裡打拚。」
心心念念在這群宏碁新血身上,施振榮對於新世代倒是充滿信心。當記者詢問施振榮,「宏碁是不是完全不一樣了?」他答得斬釘截鐵:「對。」這位為宏碁甚至為台灣帶來信心與視野的領導人,帶著笑容與對年輕人的信心回答。
我印象最深刻的禮物,是我重考上交大時,媽媽買了一部7、8,000元,在當時算是很貴,Canon的35mm單眼相機。那是我同寢室同學的哥哥從日本帶回來的。
那時候攝影沒那麼方便,用的是黑白底片,每次拍照時,包括感光度、光圈、速度跟景深都是問題。若你不懂物理,沒概念的話,拍出來的照片品質都很差。
有了這台相機,我成為同學口中的攝影高手,也開啟我對攝影的自信。我在交大成立攝影社,還有兩張照片拿到大專攝影比賽的佳作。
一張是我晚上從鹿港跑到台中拍的光復節煙火;另一張則是社團迎新時,我拍攝同學戲劇表演的過程。
這是我建立信心的里程碑。我在高中以前,根本連上台講話都不會。但在大學,我不只成立攝影社,還創立桌球隊、排球隊還有棋橋社,我都是當社長或隊長。
當時,我還辦了交大全宿舍的桌球比賽。雖然我不愛出風頭,但因為這樣,自然而然讓大家關注我的表現。等於說我不但功課不錯,玩的也很痛快。
簡單說,在哪個項目有表現,就在那累積自信心,然後擴張,這是經營人生最有效的途徑。
確立戰場,再擴大戰果
在企業品牌經營也是如此。我一直強調,台灣不缺人才、只缺舞台。那誰要建立舞台?我認為企業經營者一定要有企圖心。
但企業要提升信心,得透過累積成功而來。宏碁的「小教授一號」,是現在英國還存在的作品,我們授權他自行製造,已經在世界存在超過40年,可說是壽命最長的的電腦,這就是我們累積的能力。
從小教授一號、二號到PC,一路上,宏碁不斷累積國際化的信心。所以也是要成功,甚至有賺錢,才會有自信。
關於成功,不能訂過高的目標。我常講,我是好高騖遠,但量力務實,能力與策略都很重要。先確立一塊你能贏的戰場,累積信心再擴大戰果。
比如,林懷民是世界級的現代舞大師,但在舞台上,他考慮到東方人身材跳芭蕾舞不像西方人高䠷,所以運用太極的道理,轉而把先天限制變成差異化的特色,自然就打造出一個前所未見的創意舞團。
像宏碁推PC時候也是如此。當時,我們的策略是「鄉村包圍城市」,從東南亞、拉丁美洲、北歐、非洲先進攻,再到歐洲與美國。
因為我們在大市場資源不夠,但在鄉村反而有足夠資源。先搶到市占率,贏下一個又一個市場,就能建立團隊的信心。
信心要靠成功累積,面對失敗時才不會氣餒。宏碁也曾經遇到危機,衝市占率靠的是「量」而不是「利潤」。
市場在成長時,這樣的策略有用。但是當PC市場開始萎縮,量就變成庫存,在2013年,反過來虧了幾百億,宏碁勢必要啟動轉型。
當時,宏碁董事會啟動變革委員會,我的立場就是要JT(時任宏碁董事長王振堂)跟Jim Wong(時任宏碁總經理翁建仁)負起轉型責任。只是,要做轉型,就在這裡(指施振榮住處),他們(王振堂、翁建仁)說要辭。
所以,我就不得不接了。我屬意由陳俊聖(現任宏碁董事長)接董事長,但要他馬上當,恐怕不是很有說服力,所以我請黃少華來當一屆董事長,陳俊聖接執行長,現在就全部交給他。
走進窄門,創造成功案例
如今宏碁走的是“Dual transformation”(雙重轉型),轉型原本的事業體,但同時新事業也要進行。而且新事業的發展,都要借重原本核心事業的技術、品牌形象、通路、服務能量與管理人力,所以還是要先把這條船穩住,才能談新事業。
這是為什麼我來幫宏碁忙,因為我位高、權輕而且有影響力,也就是說,新事業初期的摸索與承擔風險責任,我來負責。當然,現在我也慢慢要全部交出去了。
身為企業領導人,要為人才打造出能進軍全世界的舞台與策略。台灣現在最有機會創造價值與成功案例的舞台,是B2B2C(編按:先服務於企業服務消費者的目的,再慢慢吸引企業的消費者,最終把他們的客戶變成最終客戶)的商業模式。
就像英特爾奔騰(Pentium)系列的CPU,大家只要聽到他「咚咚咚咚」的品牌音效,就知道是英特爾的廣告;就算對電腦品牌不了解的人,也知道“Intel Inside”的行銷口號,並且願意購買它。
但B2B2C要能形成舞台,需要很長的時間,要說服年輕人花時間與青春投入,得花功夫。最近我提出「3C而後行、5C而決策」的流程,3C就是communication、communication與communication(溝通、溝通再溝通),而5C就是再加上consensus(共識)與commitment(承諾)。
我們要說服大家,這裡有個窄門,但我們有特別的機會,切進去之後就能海闊天空。大家形成共識,才有可能突圍。
台灣年輕人最怕的,就是眼裡只看到美國、大陸或是韓國、日本的案例。所以我希望可以給他們視野,還有信心,讓台灣創造自己的成功案例。我們成功的路,要自己走出來。
▲從大學就成立攝影社,看得出來施振榮相當熱愛攝影。現在,他會在Facebook粉絲專頁分享自己用手機拍攝的作品,而他的太太葉紫華正是施振榮鏡頭下的第一女主角。
相機教我的事
用自己的興趣建立領域專長與自信。就像施振榮從攝影出發,並且擴大到其他面向上,在交通大學創立社團與球隊,並延展到日後創辦宏碁集團。這是經營人生最有效的途徑。
(本文轉載自Cheers雜誌)
https://www.cw.com.tw/article/5102849?template=transformers
流程圖template 在 流程圖範例下載2023-在Facebook/IG/Youtube上的焦點新聞和 ... 的推薦與評價
PPT流程圖模板,工作流,台階流動,11套流程圖PPT模板的. 免費實用的流程圖範本推薦- GitMind · https://gitmind.com/tw/flowchart-template.html... ... <看更多>
流程圖template 在 流程圖範例下載2023-在Facebook/IG/Youtube上的焦點新聞和 ... 的推薦與評價
PPT流程圖模板,工作流,台階流動,11套流程圖PPT模板的. 免費實用的流程圖範本推薦- GitMind · https://gitmind.com/tw/flowchart-template.html... ... <看更多>
流程圖template 在 PowerPoint『PPT免費簡報素材』魚骨圖、流程圖、動畫特效等 ... 的推薦與評價
Download this Variety Of Infographic Vector PowerPoint template for free right now! Pikbest provides millions of free PowerPoint,excel and word templates for ... ... <看更多>