61872

Read-only Functions Iteration Cost

I am developing smart contracts for some use-cases and currently I'm working on optimization of smart contracts. I am confused with something that I found interesting on Hitchiker's Guide. In the section 4- Iterating the contract code

// returns true if proof is stored // *read-only function* function hasProof(bytes32 proof) constant returns (bool) { for (uint256 i = 0; i < proofs.length; i++) { if (proofs[i] == proof) { return true; } } return false; }

For this code above, He states that "Note that every time we want to check if a document was notarized, we need to iterate through all existing proofs. This makes the contract spend more and more gas on each check as more documents are added. "

There is no doubt that the proper way of implementing it is to use mapping instead of array structure. Here is the point that makes me confused. It's read-only function and it is not a transaction that affects blockchain. When I observed my netstats, it does not show any transaction when this function is called(Actually, it is what I expected before calling this function).

I don't think that he misunderstood the mechanism, could someone clean my mind about this comment?

Answer1:

Roman’s answer is not correct. Constant functions still consume gas. However, you don’t pay for the gas usage when it runs in the local EVM. If a constant function is called from a transaction, it is not free. Either way, you are still consuming gas and loops are a good way to consume a lot.

EDIT - Here is an example to illustrate the point

pragma solidity ^0.4.19; contract LoopExample { bytes32[] proofs; function addProof(bytes32 proof) public { if (!hasProof(proof)) proofs.push(proof); } function hasProof(bytes32 proof) public constant returns (bool) { for (uint256 i = 0; i < proofs.length; i++) { if (proofs[i] == proof) { return true; } } return false; } }

And here are the gas consumption results for calling addProof 4 times:

addProof("a"): 41226

addProof("b"): 27023

addProof("c"): 27820

addProof("d"): 28617

You kind of have to ignore the very first call. The reason that one is more expense than the rest is because the very first push to proofs will cost more (no storage slot is used before the 1st call, so the push will cost 20000 gas). So, the relevant part for this question is to look at the cost of addProof("b") and then the increase with each call afterwards. The more items you add, the more gas the loop will use and eventually you will hit an out of gas exception.

Here is another example where you are only calling a constant function from the client:

pragma solidity ^0.4.19; contract LoopExample { function constantLoop(uint256 iterations) public constant { uint256 someVal; for (uint256 i = 0; i < iterations; i++) { someVal = uint256(keccak256(now, i)); } } }

Here, if you call this through Remix, you'll see something like this in the output (Notice the comment on gas usage):

<img src=https://www.e-learn.cn/content/wangluowenzhang/"https://i.stack.imgur.com/nNWLH.png" alt="Remix Output Screencap">

Finally, if you try to run this constant method from a client using too many iterations, you will get an error:

$ truffle console truffle(development)> let contract; undefined truffle(development)> LoopExample.deployed().then(function(i) { contract = i; }); undefined truffle(development)> contract.constantLoop.call(999); [] truffle(development)> contract.constantLoop.call(9999); [] truffle(development)> contract.constantLoop.call(99999); Error: VM Exception while processing transaction: out of gas at Object.InvalidResponse (C:\Users\adamk\AppData\Roaming\npm\node_modules\truffle\build\webpack:\~\web3\lib\web3\errors.js:38:1) at C:\Users\adamk\AppData\Roaming\npm\node_modules\truffle\build\webpack:\~\web3\lib\web3\requestmanager.js:86:1 at C:\Users\adamk\AppData\Roaming\npm\node_modules\truffle\build\webpack:\~\truffle-provider\wrapper.js:134:1 at XMLHttpRequest.request.onreadystatechange (C:\Users\adamk\AppData\Roaming\npm\node_modules\truffle\build\webpack:\~\web3\lib\web3\httpprovider.js:128:1) at XMLHttpRequestEventTarget.dispatchEvent (C:\Users\adamk\AppData\Roaming\npm\node_modules\truffle\build\webpack:\~\xhr2\lib\xhr2.js:64:1) at XMLHttpRequest._setReadyState (C:\Users\adamk\AppData\Roaming\npm\node_modules\truffle\build\webpack:\~\xhr2\lib\xhr2.js:354:1) at XMLHttpRequest._onHttpResponseEnd (C:\Users\adamk\AppData\Roaming\npm\node_modules\truffle\build\webpack:\~\xhr2\lib\xhr2.js:509:1) at IncomingMessage.<anonymous> (C:\Users\adamk\AppData\Roaming\npm\node_modules\truffle\build\webpack:\~\xhr2\lib\xhr2.js:469:1) at emitNone (events.js:91:20) at IncomingMessage.emit (events.js:185:7)

Answer2:

Most of the people will say constant functions will not consume gas. That will not correct because, constant functions execute on the local node's hardware using it's own copy of the blockchain. This makes the actions inherently read-only because they are never actually broadcast to the network.

Lets assume I've contract having isEmp constant function, when I call estimateGas() it should return 0 if constant method's are not consuming gas. But its returning 23301.

truffle> contractObj.isEmp.estimateGas(web3.eth.accounts[0])

23301

Here is my solidity code

function isEmp(address employee) public constant returns(bool) { return emps[employee]; }

Hence above experiment is proved that it will consume gas.

Recommend

  • CSS spacing issues with Safari?
  • recv() has no time to receive all the UDP packets from a socket on Win 7?
  • Making Toplevel resize itself to fit the title
  • Javascript Tooltip not showing up in box as defined by style
  • Use map() for functions that does not return a value
  • PayPal REST API cross reference transaction with payment
  • Camel: Bean Proxy to CXF Endpoint
  • Braintree Dropin UI does not work with Ionic Framework unless force refresh
  • FIR filter in CUDA (as a 1D convolution)
  • How to pass a custom object in REST webservice
  • How does CoreLocation locate the device?
  • Type mismatch: cannot convert from ListFragment to Fragment
  • How to set infinite shared access signature policy in azure?
  • twisted.internet.error.ConnectError when run scrapy spider
  • Is there a equivalent to JSON.Net in Java? [duplicate]
  • replacing while loop with list comprehension
  • Unique SMS sender id?
  • CSS Grid, position absolute an element in a css grid item: IMPOSSIBLE
  • end daemon processes with multiprocessing module
  • Manually Timing out a C# Thread
  • DIV instruction jumping to random location?
  • Web.config system.webserver errors
  • converting text file into xml using php?
  • std::remove_copy_if_ valgrind bytes in block are possibly lost in loss record
  • print() is showing quotation marks in results
  • Custom Tabgroup Appcelerator
  • $wpdb not working in file of WordPress plugin
  • How to convert from System.Drawing.Color to Excel.ColorFormat in C#? Change comment color
  • output of program is not same as passed argument
  • Does CUDA 5 support STL or THRUST inside the device code?
  • Statically linking a C++ library to a C# process using CLI or any other way
  • How can I estimate amount of memory left with calling System.gc()?
  • Jquery - Jquery Wysiwyg return html as a string
  • Function pointer “assignment from incompatible pointer type” only when using vararg ellipsis
  • Why winpcap requires both .lib and .dll to run?
  • 0x202A in filename: Why?
  • Arrays break string types in Julia
  • WPF Applying a trigger on binding failure
  • Java static initializers and reflection
  • File not found error Google Drive API