1

I have the following simple structure and array of that structure:

struct Document  {
      bytes32 ownerID;
      bytes32 documentID;
      bytes32 name;      
  }

  Document[] public documents;

I've a storeDoc() method that fills up the array:

function storeDocument (bytes32 _ownerID , bytes32 _documentID, bytes32 _name)  {
      Document memory newDoc;

      newDoc.ownerID = _ownerID;
      newDoc.documentID = _documentID;
      newDoc.name = _name;

      documents.push(newDoc);         

    }

And finally a getter. There could be multiple entries against one OwenerID, thus the memory arrays:

function getDocumentDetailsByID(bytes32 _ownerID) constant public returns (bytes32[], bytes32[], bytes32[]) {
      uint length = documents.length;

      if(registeredCandidates[_ownerID].clientAddress == msg.sender) {

          bytes32[] memory documentIDs = new bytes32[](length);
          bytes32[] memory names = new bytes32[](length);
          bytes32[] memory descriptions = new bytes32[](length);
          bytes32[] memory docYears = new bytes32[](length);


          for(uint i =0; i < length; i++) {

              if(documents[i].ownerID == _ownerID) {

                  Document memory currentDocument;
                  currentDocument = documents[i];

                  documentIDs[i] = currentDocument.documentID;
                  names[i] = currentDocument.name;

            }
          }


        return (documentIDs, names);  

      } else {
          return;
      }
  }

The problem I face is that only the first entry done by the storeDocument() returns using getDocumentDetailsByID() properly.

The second onward entries done by storeDocument() is coming as all zeros when I call the getDocumentDetailsByID() for the second time!!

Could someone please guide me what's I'm doing wrong? Thanks.

1 Answer 1

0

This contains an anti-pattern.

You can probably sense it growing in length and complexity. Even if the logic error is found (really not sure what the memory arrays are for) and resolved, you'll find you have still created a contract that will not scale.

The problem is your for loop iterates over the set. Each iteration costs a little gas. The cost will increase as the set grows. Eventually, this will collide with the block gas limit and the transactions will be unable to complete at all.

You can resolve this using more efficient internal structure that avoids unbounded loops. What you're doing closely resembles the Mapped Structs with Index pattern here: Are there well-solved and simple storage patterns for Solidity?

That one, or the variant with delete, will give you a scalable (same gas cost at any scale) internal storage pattern with random access by ID. If you don't need to enumerate the keys there is an even simpler pattern.

Hope it helps.

4
  • Thanks a lot. Very well explained the mistake and problem it would cause.
    – Prana
    Commented Jul 10, 2017 at 1:14
  • I have gone thru your articles and some other posts on this subject. Great design guides as they are for newcomers like me in Solidity. Nevertheless, I am wondering about the scenario I've to deal with. Here, I cannot delete the documents/entity hashes that I store (well at least for a long time!), and the records will keep on increasing as depicted by the business use case. Under these circumstances, in your opinion, is a two-way linked list would be a better proposition? Thanks a lot for your help.
    – Prana
    Commented Jul 10, 2017 at 2:26
  • In most cases I've seen, trying to replicate database features in a smart contract is a rite-of-passage. The thing is, your smart contract doesn't replace database storage. The tech solves for different problems. You may implement indexes in servers or even in browser-side caching schemes. There is no rule against this and it's exactly what happens when you visit etherscan.io or an exchange. The blockchain proves that DB/cached results are truthful. The best policy imho is to avoid complicating the immutable authoritative storage. Commented Jul 10, 2017 at 5:02
  • Right! I agree. It is better to keep historical and regularly iterate -able data and documents(blobs) in some external data store (as even the hash-key based ledgers can grow huge after certain point of time). Only point that's coming to mind is once they go off-chain (by some CRUD operation as described in your post), how to keep their sanctity and immutability. I mean in situations where the their hash-transactions are also removed from the chain. Nevertheless, as long as I can keep their hash values in the hierarchical tree, their immutability can be maintained.
    – Prana
    Commented Jul 10, 2017 at 13:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.