在 Solidity 智能合約中儲存和檢索大數據的最佳實踐是什麼?
假設,我有一個事件創建頁面,在 UI 中有一些表單欄位。而且我有一個智能合約,可以將事件儲存在事件映射或某種結構格式的公共數組中。
struct event{ bytes32 name; uint time; } event[] public events; or mapping(uint=>event) public events;
我正在送出 UI,數據儲存在契約中。
我有一個列表事件頁面,它將顯示契約中創建的所有事件。
在搜尋了許多論壇後,我得到了一些資訊,有人可以驗證這一點。
方法一:
對於合約中的每次送出或保存,都會呼叫一個事件,並且 UI 具有觀察者並儲存在本地儲存中並顯示在 UI 中。事件數據在這裡可以是映射或數組。
我不想為應用程序提供任何中間儲存,因此每當 UI 刷新時,事件觀察器將接收所有狀態數據並再次更新本地儲存並在 UI 中顯示。
方法二:
對於事件送出,創建新的事件對象並將其推送到事件數組中。
並編寫 get events 方法,迭代事件數組並返回對像中每個鍵的字元串元素列表,然後再次連接和解構 UI 中對像數組中的字元串以與 UI 綁定。
我認為由於迭代,第二種方法的計算量會很高。由於我們正在開發 Dapp,我不想再有任何中間數據庫成為一個集中式系統。
有人可以解釋從智能合約中儲存和檢索數據的各種可能性,這是迄今為止最好的方式。
這對我有很大幫助。提前致謝。
event
是一個可靠的關鍵字,可讓您輕鬆檢索契約在 Javascript 前端中生成的數據。就 gas 而言,寫一個事件比寫一個儲存變數(比如你的struct
數組)更便宜。然後,您可以.watch
針對這些事件並將它們拉入您的 UI。這是推薦的方式。出於性能原因,您可能希望以某種方式記憶體這些事件數據,因為每次重新載入時解析所有塊的效率相當低。此外,不要嘗試將所有數據放入智能合約中。一個設計良好的 Dapp 的工作就是只包含絕對需要的元素和指向外部結構的指針,但要讓你的儲存層與區塊鏈分開。儲存層可以通過IPFS或Swarm實現。
正如 Sebastian 所說,您要謹慎對待要在合約中儲存在區塊鏈上的數據。
話雖如此,儲存數據的 gas 成本將落在交易發行者身上以創建事件。同樣,任何需要迭代此數據的基於“事務”的呼叫都將變得非常昂貴,具體取決於數據量和您需要執行的迭代。
如果一個函式被標記為常量並且不需要修改數據,則可以在不產生 gas 成本的情況下呼叫它,因此迭代事件列表雖然計算成本很高,但如果作為交易執行,則基本上可以自由呼叫。
一個好的做法是添加一個您想要列出的範圍,以便您可以限制每次呼叫所需的處理並在您的 dApp 中分頁結果。
這是一個可以做到這一點的合約範例,它絕不是完整的,但 Remix 中的複制粘貼將用於顯示基本功能:
pragma solidity ^0.4.0; contract EventManager { struct Event { bytes32 name; uint time; } uint totalEvents; mapping(uint => Event) EventList; event EventAdded(address indexed _senderAddress, uint _eventId); function addEvent(bytes32 _name, uint _time) returns(uint eventId) { eventId = totalEvents++; EventList[eventId] = Event(_name, _time); EventAdded(msg.sender, eventId); } function listEvents(uint _start, uint _count) constant returns(uint[] eventIds, bytes32[] eventNames, uint[] eventTimes) { uint maxIters = _count; if( (_start+_count) > totalEvents) { maxIters = totalEvents - _start; } eventIds = new uint[](maxIters); eventNames = new bytes32[](maxIters); eventTimes = new uint[](maxIters); for(uint i=0;i<maxIters;i++) { uint eventId = _start + i; Event memory e = EventList[eventId]; eventIds[i] = eventId; eventNames[i] = e.name; eventTimes[i] = e.time; } } }