函式呼叫輸出有限制嗎?
我有一個簡單的契約,用於在數組中儲存 ipfs 雜湊數據的字元串元組
pragma solidity ^0.4.24; pragma experimental ABIEncoderV2; contract Store { string[] public objects; function setData(string memory x, string memory y) public { objects.push(x); objects.push(y); } function getDataAtIndex(uint256 index) public returns (string memory, string memory) { return (objects[2 * index], objects[2 * index + 1]); } function getCount() public returns (uint count) { return objects.length / 2; } function getAllData() constant returns (string[]) { return objects; } }
我正在使用
getAllData()
一次獲取整個數組。當數組的大小很小時,一切正常。但是當我有大數組時,它會引發錯誤web3.exceptions.BadFunctionCallOutput:無法解碼合約函式呼叫 getAllData 為 output_types 返回數據 b''
$$ ‘string[ $$’]
你知道是什麼導致了這個問題嗎?
有沒有更好的方法在一份合約中儲存大量 ipfs 雜湊?還是將其儲存在更多合約中並蒐索所有合約更好?
一般來說,“退貨”是不必要且不可靠的。
它在精神上與 EVM 相似,
SELECT * FROM OBJECTS
但不是特別適合 EVM。以數據庫為中心的世界是集中式數據庫和相對便宜(成本和性能)的操作之一。EVM 由具有複製狀態副本和不同成本基礎和性能配置文件的節點組成。那麼,為什麼沒有必要呢?
您可以假設所有節點(所有感興趣的觀察者)都可以並且應該知道所有狀態更改。即所有的插入、更新和刪除操作。這是因為所有相關方都可以觀察到所有事務(在以數據庫為中心的設置中不適用這樣的假設)。您可以通過事件更方便地觀察重要的狀態變化。
event LogSetData(string value); function setData(... object.push(... LogSetData(x); ...
訂閱者可以“監聽”從部署合約的區塊到目前區塊以及未來的事件日誌,這樣任何插入交易都不會逃過他們的通知。因此,客戶端可以確保他們已經知道陣列的狀態。那麼問的意義何在?
您已經有了一個可發現的長度和一種在特定索引處檢查數據的方法(有點奇怪,但它就在那裡)。當客戶是另一個契約時,這將是合適的。另一個合約對複制整個陣列不感興趣(成本太高),因此,再次,無需一次性訪問整個陣列。
為什麼不可靠?
Gas 核算適用於只讀操作,並且適用 gasLimit 規則。因此,數組的大小有一個上限。這意味著對可擴展性的限制。如果數組太大,函式會失敗。
確實,gas 將在只讀操作中退還(從未實際花費)。這是一個令人困惑的話題。更多資訊:https ://blog.b9lab.com/getting-loopy-with-solidity-1d51794622ad
由於我們知道輸入和輸出的數組大小都有限制,因此提供替代方案總是一個好主意。如果對替代方案進行編碼,則通常會發現不需要龐大的功能。您的
getDataAtIndex()
和getCount()
函式使客戶端能夠迭代對象。嚴格來說,getAllData()
不需要,這是一件好事,因為它不適用於大型數組。您可能已經註意到,動態數組有時用於批處理操作。例如,作為空投的輸入,陣列可以從操作中擠出幾滴氣體,這些操作一次一行完成的成本略高。重要的是,在這種情況下,發送者可以控制批量大小,因此發送者可以在找到他們可以安全發送的最大批量大小後相應地管理自己。
相同的邏輯不適用於您的 read 函式,因為它會全部返回。這不會像數據庫那樣快,因為節點需要在區塊鏈中四處尋找所有元素。它只是沒有為此進行優化。它無法將返回的行限制為在超過塊 gasLimit 之前完成的量級(沒有
from
,to
),因此當列表變得太大時它只會失敗。希望能幫助到你。