[爆卦]struct陣列是什麼?優點缺點精華區懶人包

雖然這篇struct陣列鄉民發文沒有被收入到精華區:在struct陣列這個話題中,我們另外找到其它相關的精選爆讚文章

在 struct陣列產品中有3篇Facebook貼文,粉絲數超過3,460的網紅Taipei Ethereum Meetup,也在其Facebook貼文中提到, 📜 [專欄新文章] [zkp 讀書會] Cairo 語言介紹 ✍️ NIC Lin 📥 歡迎投稿: https://medium.com/taipei-ethereum-meetup #徵技術分享文 #使用心得 #教學文 #medium Cairo 是 STARK 證明系統的其中一個編程語言,...

  • struct陣列 在 Taipei Ethereum Meetup Facebook 的最佳解答

    2021-06-29 02:57:14
    有 9 人按讚

    📜 [專欄新文章] [zkp 讀書會] Cairo 語言介紹

    ✍️ NIC Lin

    📥 歡迎投稿: https://medium.com/taipei-ethereum-meetup #徵技術分享文 #使用心得 #教學文 #medium

    Cairo 是 STARK 證明系統的其中一個編程語言,讓開發者能透過 Cairo 來使用 STARK,撰寫效能更高的 Dapp

    Photo by Simon Berger on Unsplash

    Warning:本篇會保持在 high level 的介紹,實際深入的部分請見文內附上的文檔或是官方開發者文件

    背景介紹

    建構於密碼學的零知識證明能提供計算的隱私性,但同時在區塊鏈生態系也被用來提升 Scalability — 我可以用 10 秒的運算資源來驗證原本耗費 1000 秒運算資源的計算過程

    如同更多人熟悉的 SNARK,STARK 也是一個零知識證明的證明系統,但當前的 STARK 著重的是在 Scalability ,而非大家比較習以為常零知識證明提供的隱私性特質

    其實目前基於 SNARK 的 Rollup 項目,例如 zkSync、Loopring、Aztec、zkopru,除了 Aztec 外,其他都是利用 SNARK 來增加 Scalability — 這些 Rollup 上資料都還是公開、沒有隱私性的

    StarkWare 是目前唯一基於 STARK 的開發團隊

    STARK 要加上隱私保護不會太難,只是 StarkWare 還沒有把這項功能放在未來規劃中

    Cairo 簡介

    標榜為圖靈完備的零知識證明系統語言,Cairo 對原本熟悉 Solidity 的開發者來說還是會感到比較難上手和陌生的。再加上套件庫還不夠充足,目前支援的雜湊函式是 Pedersen,數位簽章演算法是 ECDSA(相對於 SNARK,EdDSA 的效能反而比較差所以沒有支援)。
    但 Cairo 還在早期開發的階段,相信開發體驗會越來越好的。

    另外需要注意的是作為一個證明系統,會有 Prover 和 Verifier 的角色。而 STARK 的 Verifier 是公開的,但 Prover 軟體預計會有 License 保護。Prover 一般情況下不得用於商業用途,除非將 proof 上傳至官方的 Verifier。

    最後要提及的是,第一版的 Cairo 是設計來方便開發者將 Dapp 的運算遷移至鏈下。不同於 Rollup,這個鏈下只會有它自己一個 Dapp。這個 Dapp 的項目方自己維護自己 Dapp 的 state。( Rollup 則是 operator 維護所有 Dapp 的 state,Dapp 開發者不需自己操煩)
    這可能有點難懂。如果你有在寫 Solidity,想像一下今天你在合約要用到合約裡宣告的 storage 變數時,你要自己提供 merkle proof 上來,證明這個storage 變數真的是這個值。這個就是開發者要自己維護 state 的意思。

    而第二版的 Cairo 則是 StarkNet 裡使用的 Cairo(第一和第二版是不同編譯器),這版的 Cairo 就是作為 Dapp 在 Rollup 開發所使用 — 開發者可以在合約裡宣告變數,變數的值不需開發者維護,可以直接假設存在。
    註1:StarkWare 不喜歡 Rollup 這個詞,他們覺得 Data Availability 的需求是一段光譜:不一定得要把 data 全都送上 L1,中間有其他方式可以做不同層級的 Data Availability。
    註2:第一版和第二版實際上在官方版本裡是 0.0.1 及 0.0.2,在撰文當前最新版即是 0.0.2

    官方網站:https://www.cairo-lang.org
    開發者文件:https://www.cairo-lang.org/docs/

    開發環境

    Cairo 有提供像是 Remix 的瀏覽器 IDE:playground。裡面提供各種範例練習和挑戰,除了可以編譯,還可以直接生成並上傳 proof。
    註:但有些功能還是沒辦法在 playground 裡使用,例如要給你的程式 custom input 時。這時候只能在本地端開發才能使用這個功能。

    開發 Cairo 要先安裝python,我將開發者文件整理出來的資料統整在這個 hackmd 文檔裡:https://hackmd.io/w690dpAQTsKeKZv3oikzTQ
    裡面包含簡介、設置本地開發環境以及 Cairo 基礎(因為篇幅原因,所以不將內容複製到這裡)
    註:我把開發者文件裡的代碼整理到這裡:https://github.com/NIC619/cairo_practice/tree/master/practices
    如果不想在研究開發者文件過程中,還要自己手動拼湊裡面例子的話,可以直接用整理好的代碼來執行。同時 repo 裡還有包含一些額外自己測試 Cairo 功能的範例。

    深入 Cairo

    在那份 hackmd 文檔裡的開頭,可以連結到第二部分 — 深入 Cairo 的部分。裡面也是從開發者文件裡擷取出來我覺得比較重要的部分。如果你要讀開發者文件的話,我建議從 Hello Cairo 開始,它會從例子切入,會比較好知道 Cairo 怎麼使用。接著如果要更深入了解,再去讀 How Cairo Works。

    StarkNet Cairo

    第二版的 Cairo 其實功能和第一版的 Cairo 是差不多的,所以不必擔心在開發者文件裡學到的 Cairo 在 StarkNet 版本會不能用或差很多。在讀完 Hello Cairo/How Cairo works 後,就可以接著看 Hello StarkNet。會很順利的切換到 StarkNet 版本的 Cairo。
    註1:我整理的文檔裡是按照第一版 Cairo 所寫的
    註2:如果你從開發者文件一路看下來,體驗過非 StarkNet 版的 Cairo,那你在體驗 StarkNet 版的 Cairo 時一定會發現這更像一般智能合約的使用方式 — 你可以用 view 函式查詢 storage 變數,可以用 external 函式去執行合約(非 StarkNet 版本不是這樣操作 Dapp 的,這邊因為篇幅原因沒有詳細介紹)。

    非常建議嘗試兩種版本的 Cairo,你會知道 1. 操作一個單獨在 L2 的 Dapp 和2. 操作與其他 Dapp 共存在 Rollup 上的 Dapp 的不同。這對了解 L2 怎麼運行、需要哪些資料、為什麼需要這些資料非常有幫助。

    0.0.2 版的 StarkNet Cairo 目前還缺少一些功能:

    函式還沒辦法宣告陣列或 struct 型態的參數

    合約和合約之間還沒辦法互動

    L1 沒有辦法讀取到 L2 的資料,L2 也沒辦法讀取到 L1 的資料。如果要建立跨 L2 Bridge,這個功能非常重要。

    補充及個人心得

    STARK 的 proof size 相比於 SNARK 系列的 proof size 大很多,又其證明所包含的交易數量對 proof size 和驗證時間的影響不大,所以把很多筆交易一併做一個 proof 會是對 STARK 非常有利、節省成本的方式(SNARK、STARK 比較表)。但這同時也是一個缺點,如果你的 Dapp 或 Rollup 的 TPS 不高,那就只能等更久時間搜集多一點的交易,要不然就只能提高成本來維持驗證 proof 的頻率。

    StarkWare和 zkSync 一樣都有 Rollup 宇宙的概念( Rollup 宇宙的用詞並不精確,因為在他們的宇宙中不會所有子鏈都是 Rollup,而是會有依照 Data Availability 程度不同所區分的子鏈,像是 Validium、zk Porter 的設計),個人覺得能夠有(針對 Data Availability 程度的)選擇是會比只有一個選擇(完全 Data Available) 還好的方式,但實際上的可行性就要等其團隊釋出更多的資訊。

    在 Rollup 越趨成熟的情況下,能夠提供快速跨 Rollup 服務的流動性提供者的角色會越來越重要。zk Rollup(StarkNet、zkSync、etc…)比 Optimistic Rollup (Optimism、Arbitrum、etc…)有著短上許多的 finalize 時間,這對降低流動性提供者的風險有很大的幫助,但目前 zk Rollup 支援合約功能甚至 L1 <-> L2 互動的完成度都比 Optimistic Rollup 還低上許多。短期內快速跨 Rollup 的服務應該還是侷限在 Optimitic Rollup 之間。

    abbrev

    [zkp 讀書會] Cairo 語言介紹 was originally published in Taipei Ethereum Meetup on Medium, where people are continuing the conversation by highlighting and responding to this story.

    👏 歡迎轉載分享鼓掌

  • struct陣列 在 Taipei Ethereum Meetup Facebook 的最讚貼文

    2021-01-11 20:57:40
    有 20 人按讚

    📜 [專欄新文章] 可升級合約介紹 - 鑽石合約(EIP-2535 Diamond standard)
    ✍️ Kimi Wu
    📥 歡迎投稿: https://medium.com/taipei-ethereum-meetup #徵技術分享文 #使用心得 #教學文 #medium

    Photo by Evie S. on Unsplash

    前言

    可升級合約簡單來說是透過 proxy contract(代理合約)來達成,藉由代理合約去呼叫欲執行的合約,若要升級,則把代理合約中的指向的地址換為新的合約地址即可。而執行的方式則是透過 delegateCall,但 delegateCall 不會更動目標合約的狀態。所以要怎麼處理變數,就是一門學問了。

    舉例來說,contract B 有個變數 uint256 x,初始值為 0, 而 function setX(uint256),可以改變 x 的值。proxy contract A 使用 delegatecall 呼叫 contract B 的 setX(10),交易結束後,contract B中的 x 依然還是 0。

    OpenZeppelin 提出了三種實作方式,可以做到可升級合約,細節可參考 Proxy Patterns,而最終的實作選用了 Unstructured Storage的這個方式,這種方式對於開發較友善,開發時不需特別處理 state variables(不過升級時就需要特別注意了)。而這篇主要是介紹 Diamond standard,OpenZeppelin 的可升級合約就不多做介紹。

    USDC V2 : Upgrading a multi-billion dollar ERC-20 token 詳細地介紹代理合約跟變數儲存之間的關係,不了解升級合約的原理,建議先看看。

    鑽石合約

    名詞介紹

    diamond:合約本體,是一個代理合約,無商業邏輯

    facet:延伸的合約(實際商業邏輯實作的合約)

    loupe:也是一個 facet,負責查詢的功能。可查詢此 diamond所提供的 facet與facet所提供的函式

    diamondCut:一組函式,用來管理(增加/取代/減少)此 diamond合約所支援的功能

    Loupe

    直接來看 loupe的介面,從宣告就能很清楚暸解 diamond合約的實作方式,loupe宣告了一個結構 Facet,Facet結構包含一個地址及 function selector 陣列,所以我們只需要記錄一個 Facet陣列就可以得知這個 diamond 合約有多少個延伸合約及所支援的功能(loupe只定義結構,而實際變數是存在diamon合約中的)。也就是 diamond合約中只記錄延伸合約的地址及其支援的 function selectors,及少數 diamond合約的管理邏輯,並無商業邏輯,因此可以外掛非常非常多的合約上去(就像一個Hub),也就可以突破一個合約只有24K的限制。

    // A loupe is a small magnifying glass used to look at diamonds.interface IDiamondLoupe { struct Facet { address facetAddress; bytes4[] functionSelectors; } function facets() external view returns (Facet[] memory facets_); function facetFunctionSelectors(address _facet) external view returns (bytes4[] memory facetFunctionSelectors_); function facetAddresses() external view returns (address[] memory facetAddresses_); function facetAddress(bytes4 _functionSelector) external view returns (address facetAddress_);}

    DiamondCut

    至於 facet在 diamond合約上的註冊或是修改,就由 diamondCut負責,從以下程式碼可以清楚瞭解其功能(EIP中有規範,每次改變都需要發送DiamondCut事件)

    interface IDiamondCut { enum FacetCutAction {Add, Replace, Remove} // Add=0, Replace=1, Remove=2 struct FacetCut { address facetAddress; FacetCutAction action; bytes4[] functionSelectors; } function diamondCut( FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata ) external; event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);}

    Diamond合約

    接下來就是最核心的部分 — diamond本體合約。以下是官方的範例,方法上跟 OpenZeppelin 一樣使用 fallback 函式跟 delegateCall 。

    呼叫合約所不支援的函式,就會去執行 fallback 函式,fallback 函式中再透過 delegateCall 呼叫 facet 合約相對應的函式

    fallback() external payable { address facet = selectorTofacet[msg.sig]; require(facet != address(0)); // Execute external function from facet using delegatecall and return any value. assembly { calldatacopy(0, 0, calldatasize()) let result := delegatecall(gas(), facet, 0, calldatasize(), 0, 0) returndatacopy(0, 0, returndatasize()) switch result case 0 {revert(0, returndatasize())} default {return (0, returndatasize())} }}

    主要的差異在於變數的處理,OpenZepplin 是針對單一合約設計的代理合約(也就是每個合約都有自己的代理合約),所以無法處理單一代理合約儲存多個合約的變數(state variables)的狀況(後有圖例)。先由官方的範例程式來了解是怎麼處理變數的

    在官方的範例中,都是以更改合約 owner 為例子

    首先看到 DimaondStorage這個結構,結構中的前面三個變數都是在維持 diamond合約的運作(同上面loupe的範例),最後一個變數 contractOwner就是我們商業邏輯中所需的變數。
    接著看到 function diamondStorage(),取變數的方式就跟OpenZeppelin 儲存特定變數方式一樣(EIP-1967),是把變數存到一個遠方不會跟其他變數碰撞到的位置,在這裡就是從 DIMOND_STORAGE_POSITION 這個 storage slot 讀取。
    在實作上就可以有 LibDiamond1 ,宣告DIMOND_STORAGE_POSITION1=keccak256("diamond.standard.diamond.storage1") ,負責處理另一組的變數。藉由這種方式讓每個 facet合約有屬於自己合約的變數, facet合約間就不會互相影響。而最下方的 setContractOwner 是實際使用的範例。

    library LibDiamond {

    bytes32 constant DIAMOND_STORAGE_POSITION = keccak256("diamond.standard.diamond.storage");

    struct FacetAddressAndSelectorPosition { address facetAddress; uint16 selectorPosition; }

    struct DiamondStorage { mapping(bytes4 => FacetAddressAndSelectorPosition) facetAddressAndSelectorPosition; bytes4[] selectors; mapping(bytes4 => bool) supportedInterfaces; // owner of the contract address contractOwner; }

    function diamondStorage() internal pure returns (DiamondStorage storage ds) { bytes32 position = DIAMOND_STORAGE_POSITION; assembly { ds.slot := position } }

    function setContractOwner(address _newOwner) internal { DiamondStorage storage ds = diamondStorage(); address previousOwner = ds.contractOwner; ds.contractOwner = _newOwner; emit OwnershipTransferred(previousOwner, _newOwner); }

    每個 library 處理了一組或多組變數的存取, facet 合約透過 library 對變數做操作。也就是把變數存在diamond主體合約,延伸的 facet合約只處理邏輯,是透過 library 去操作變數。

    下面圖中清楚地解釋了 facet合約,function selectors 與變數之間的關係,從最左上這邊有個 facets 的 map,紀錄了哪個 selector 在哪個合約中,例如func1, func2是 FacetA的函式。左下角宣告了變數,每組變數的存取如同上述 library 的方式處理。

    https://eips.ethereum.org/EIPS/eip-2535#diagrams

    在 diamond的設計中,每個 facet合約都是獨立的,因此可以重複使用(跟library 的概念一樣)

    https://eips.ethereum.org/EIPS/eip-2535#diagrams

    小結

    diamond合約使用不同的設計來達成合約的可升級性,藉由這種Hub方式可隨時擴充/移除功能,讓合約不再受限於24KB的限制,此外充分的模組化,讓每次升級的範圍可以很小。最後,因為跟library一樣只處理邏輯,並無狀態儲存,所以可以重複被不同的diamond合約所使用。

    雖然又不少好處,也是有些缺點。首先,術語名詞太多,facet, diamondCut, loupe等等(其實還有好幾個,不過沒有介紹到那些部分,所以沒有寫出來)。開發上不直覺,把變數跟邏輯拆開,若要再加上合約之間的繼承關係,容易搞混,不易維護。最後,gas的花費,在函式的讀取、呼叫,變數的存取、傳遞都會有不少的額外支出。Trail of Bits 專欄中有點出更多的缺陷 Good idea, bad design: How the Diamond standard falls short,不過作者也有反擊 Addressing Josselin Feist’s Concern’s of EIP-2535 Diamond Standard,有興趣的讀者可以自行看看、比較。

    為了模組化及彈性,diamond合約在設計上有點太複雜(over engineering),會造成可讀性越差(這點也是Vyper誕生的原因之一),而可讀性越差就越容易產生bug、也越不容易抓到bug,而在defi專案中,一個小小的bug通常代表著大筆金額的損失 😱😱😱。

    雖然如此,筆者還是覺得很酷,有些設計的思維仍然可以使用在自己的專案

    ref:
    EIP 2535
    Diamond 實作
    Addressing Josselin Feist’s Concern’s of EIP-2535 Diamond Standard
    OpenZeppelin upgradeable contract

    可升級合約介紹 - 鑽石合約(EIP-2535 Diamond standard) was originally published in Taipei Ethereum Meetup on Medium, where people are continuing the conversation by highlighting and responding to this story.

    👏 歡迎轉載分享鼓掌

  • struct陣列 在 Taipei Ethereum Meetup Facebook 的最佳貼文

    2018-08-23 20:35:20
    有 3 人按讚


    📜 [專欄新文章] Solidity Weekly #9
    ✍️ mingderwang
    📥 歡迎投稿: https://medium.com/taipei-ethereum-meetup #徵技術分享文 #使用心得 #教學文 #medium

    什麼時候用 storage,什麼時候該用 memory?

    其實這副標題,有可能又會誤導大家如何寫 solidity。簡單講它們是完全不一樣的東西。所以應該會用在不同的地方,做不同的事才對。

    先簡單說明它們的原理:

    storage 就是 smart contract 正常存放狀態 (state) 的地方,而這些地方就是用來呈現在區塊鏈裡狀態值的變更。所以一般來說,全域變數就會用 storage 來儲存。

    它以 32 bytes 為單位類似 hash 的方式做 key/value 的查詢。所以即時key 1 跟 key 1000,所用的空間跟 key 1 跟 key 2 一樣。(所花的 gas 也應該相同)

    另外,通常方程式帶入的變數或回傳值,會以 memory 方式表示,或有些 compiler 會用 stack 來儲存 (不花 gas)。若帶入變數內容來自於一個全域變數,它會複製一份 storage 裡的資料到 memory 做修改。但如果你在方程式帶入的變數前用 storage 保留字來描述,它就變成 passed by reference,把 storage 的位置直接傳給該方程式,因此所有的變動,都會直接改到 storage 裡的值。

    而一般在方程式裡的本域變數 (local variables),也預設用 storage 來處理,除非你故意用 memory 保留字來定義它。但用 memory 來處理陣列會有點困難。不像 storage 的陣列,可以用 push。(如下錯誤範例)

    function createRoom() public{ address[] memory adr; adr.push(msg.sender); // compiler error ...}

    但為了節省 gas 的消耗,在方程式裡本域變數改用 memory 也是很常見的事。

    links 分享;

    Ethereum Solidity: Memory vs Storage & How to initialize an array inside a struct — (Georgios Konstantopoulos)

    Solidity Introduction — (A very good Solidity tutorial from BitDegree)

    New Features of Solidity 0.5.0 — (@Christian Reitwiessner)

    Solidity Weekly #9 was originally published in Taipei Ethereum Meetup on Medium, where people are continuing the conversation by highlighting and responding to this story.

    👏 歡迎轉載分享鼓掌

你可能也想看看

搜尋相關網站