3

I am trying to interact with a basic certifications contract that stores certifications using truffle and when I deployed this contract on Remix, everything works.

But as soon as I deploy it locally, the certifications are sent (with successful transaction hashes returned), but none of the getters are working as expected.

Here are the relevant parts of the contract:

    pragma solidity ^0.4.2;

/// @title Sending and storing certifications to the blockchain
contract CertificationStore {

struct Certification {
    string firstName;
    string lastName;
    string certName;
    string institution;
    string description;
    uint year;
    uint month;
    uint day;
}

mapping(bytes32 => Certification) public certificateStructs;
mapping(address => bytes32[]) public certificatesByAddress;

Certification[] public certArray;
address[] public addressIdx;
bytes32[] public certificateIdxList;

uint numCerts;

event Send(address indexed _from);
event logIdx(address indexed _from, string location, uint idxToLog);
event logString(address indexed _from, string location, string msg);
event logBytes32(address indexed _from, string location, bytes32 theBytes);

function CertificationStore() {
    numCerts = 0;
}

function getCertArrayLength() constant returns(uint) {
    return certArray.length;
}

function getAddressIdxLength() constant returns(uint) {
    return addressIdx.length;
}

function getNumberOfCerts() constant returns(uint) {
    return numCerts;
}

function getCertificateIdxListLength() constant returns(uint) {
    return certificateIdxList.length;
}

function getLatestBlob() constant returns(bytes32) {
    return certificateIdxList[certificateIdxList.length -1];
}

function getLatestFirstName() constant returns(string) {
    bytes32 id = getLatestBlob();
    if(id.length == 0) {
        return "id is 0. no blob id was created";
    } 

    bytes memory tempEmptyStringTest = bytes(certificateStructs[id].firstName);
    if(tempEmptyStringTest.length == 0) {
        return "the first name string is empty";
    }

    return certificateStructs[id].firstName;
}

function sendCert(address acct, string fn, string ln, string cn, 
    string ins, string desc, uint yy, uint mm, uint dd) returns(uint) {

    bytes32 blobId = generateIdx();

    certificateStructs[blobId] = Certification({
            firstName: fn,
            lastName: ln,
            institution: ins,
            description: desc,
            certName: cn,
            year: yy,
            month: mm,
            day: dd
        });

    numCerts++;

    addressIdx.push(acct);
    certificateIdxList.push(blobId);
    certArray.push(Certification({
            firstName: fn,
            lastName: ln,
            institution: ins,
            description: desc,
            certName: cn,
            year: yy,
            month: mm,
            day: dd
        }));

    logIdx(acct, "in Send Cert. numCerts ", numCerts);
    logBytes32(acct, "in SendCert, blob id is", blobId);
    Send(acct);

    return numCerts;
}

function getFirstName(address _from) constant returns(string) {

    for (uint i = 0; i < certArray.length; i++) {
        Certification cert = certArray[i];
        string name = cert.firstName;
        address acct = addressIdx[i];
        if(acct == _from) {
            return name;
        }
    }

    return ("no name found");
}

function generateIdx() internal returns(bytes32) {
    bytes32 blobId = bytes32(keccak256(msg.sender, block.blockhash(block.number - 1)));
    // Make sure this blobId has not been used before (could be in the same block).
    bytes32 temp;
    for (uint i = 0; i < certificateIdxList.length; i++) {
        temp = certificateIdxList[i];
        if(blobId == temp) {
            blobId = bytes32(keccak256(blobId)); // if collision, rehash it. 
        }
    }

    return blobId;
}
}

In an older post, I changed the way my frontend is calling these methods to use .call() but 2 unexpected things are happening:

  1. All of the get methods are returning 0. So this makes it seem that, even though a certification is being sent to the block chain, the arrays are not getting updated for some reason??

  2. The method getLatestFirstName is throwing an unexpected error:

new BigNumber() not a base 16 number

even though that method returns a string....

This is how I am instantiating the contracts using truffle and web3 in the frontend:

import { default as Web3} from 'web3';
import { default as contract } from 'truffle-contract';

import certification_store_artifacts from '../../build/contracts/CertificationStore.json';

var CertificationStore = contract(certification_store_artifacts);

window.App = {
  start: function() {
    var self = this;

    CertificationStore.setProvider(web3.currentProvider);

    --- some other irrelevant code here ---
},

// example function call of a getter:

getNumCerts: function() {
    var self = this;
    var meta;

    CertificationStore.deployed().then(function(instance) {
      meta = instance;
      return meta.getNumberOfCerts.call();
    }).then(function(value) {
        console.log("result of getting num certs ", value);
      }).catch(function(e) {
        console.log(e);
      });
  },

Whenever i call that getNumCerts method, it ALWAYS returns a BigNumber with the value 0.

this is how I am sending a certification to the blockchain:

sendCert: function() {
    var self = this;
    this.setStatus("Initiating certification... (Please wait)");

    var meta;
    var myEvent;


    CertificationStore.deployed().then(function(instance) {
      meta = instance;
      var firstName = document.getElementById("first").value;
      // get all the other values from the UI form as well

      var tempDate = new Date(date);

       return meta.sendCert(account, firstName, lastName, certName, institution, description, tempDate.getFullYear(), tempDate.getMonth()+1, tempDate.getDay(), {from: account});
    }).then(function(txn) {
      // on success
      alert("Your certification was successfully sent for account: " + account);
      console.log("txn of your deployment ", txn);

      self.setStatus("Sent your certification successfully for account: " + account); // this account is defined well before. this is working fine
    }).catch(function(e) {
      // on error set status
      console.log(e);
      self.setStatus("Error sending certification; see log.");
    });
  }, 

Sorry for the long winded question. This has become really, really confusing to me since I can't see a reason this shouldn't work.

Is it a local environment issue? I am using Geth and every time I send a transaction, it mines the transaction and returns a transaction hash properly.

  • 2
    Did you check transaction failed execution? ie an out of gas exception. Try using a higher gas value like 2M for the transaction: meta.sendCert(account,.., {from: account, gas: 2000000}. Check the transaction receipt for the 'status' field and gas used. If the transaction failed try increasing the gas. – Ismael Dec 6 '17 at 3:05
  • omg...you are a genius. I upped the gas from the original 3M to 5M and it worked!!! WHY?!?!?! It never threw an out of gas exception before! Is there any other way to tell?? – Karan Dec 6 '17 at 3:55
  • I've been there in your same place before. Usually using strings is expensive and the default gas is not enough. Pass the knowledge forward! – Ismael Dec 6 '17 at 4:40
  • @Ismael thanks for that gas tip, related is that sometimes estimateGas() is incorrect if there is related constructor or other hidden function calls (haven't gone deep into detail on that yet). – robertdavid Nov 21 '18 at 15:09
  • 1
    @robertdavid Since a previous fork to measure gas is more complex. Part of the gas can reimbursed but you have to specify the maximum gas. So it is possible a transaction that uses 90k to fail if you specify just 90k, because the maximum gas uses is 100k, and 10k are reimbursed 'after' the transaction is finished. – Ismael Nov 21 '18 at 20:13
2

Ok thanks to the comments above, I was able to solve the issue.

I upped the gas from 3M to 5M in my truffle.js file and it worked. It never threw an out of gas exception before, I was getting back a transaction hash and everything!

But after that change, the data was successfully pushed onto the chain.

My lingering question though is: in the future, how can i check if there is enough gas being sent with the transaction? The transaction information that was returned to just said

gasUsed: 90000

gas: 90000

No exception was thrown at all!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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