0

I have written a basic contract like this to store certificates:

    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 (address => Certification) public certifications;

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

        uint numCerts;

        Certification public aCert; 

        event Send(address indexed _from);

        function CertificationStore() {
        }

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

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

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


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

            certId = numCerts++;

            addressIdx[certId] = acct;
            certArray[certId] = Certification({
                    firstName: fn,
                    lastName: ln,
                    institution: ins,
                    description: desc,
                    certName: cn,
                    year: yy,
                    month: mm,
                    day: dd
                });
            Send(acct);

        }

        function getFirstName(address _from) public 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");
        }

    }

I am using web3 and truffle to create a frontend to interact with this contract.

On the frontend, when I call sendCert with all the values, it seems to add them to the map but NOT to the array. I tried adding to the array the way shown above and with the .push() method and when I call the two functions to check the certArray length and the addressIdx array length, it ALWAYS returns 0.

The method sendCert, which is supposed to add the certifications to the array and the mapping also returns 0 for that certId no matter how many times I add certifications.

Am I doing something wrong here? In the frontend, I have a call back which logs "success" when the certification is sent successfully and executed by this method successfully.

Lastly, is there any way I can send the contents of the two arrays to the frontend to work on them in javascript instead of solidity?

Thank you for your help and patience!

1 Answer 1

1

You were on the right track with .push.

addressIdx.push(acct);
certArray.push(Certification({

There's a lot more going here. The big picture item is you're storing the certificate structure twice - once in the mapping and once in an array. This is not ideal. Also, I think the use of an address for the unique identifier of each certificate causes limitations and confusion. Why is a certificate known by the id of someone who earned it? What if a student earns more than one certificate? It's probably better to reference each certificate with an arbitrary Id:

mapping(bytes32 => CertificateStruct) public certificateStructs;

and then you can maintain an iterable list of them:

bytes32[] public certificateList;

You might also want a list of certificates for each student address, which could lead to something like:

mapping(address => bytes32[]) studentCertificates;

Your read-only functions like function getAddressIdxLength() returns(uint) are not marked with the constant modifier, so you're not getting the expected uint. It's costing gas to send a transaction and you don't get to see the response owing to mining delays. When you add constant, you'll see the expected 1 and things will start to make sense again.

It's a little on the data-heavy side for a platform with expensive storage cost and you might find that a mapping from an address to a certificate Id, in concert with off-chain storage adequately solves the problem of proving a given student was awarded a given certificate at a given time. The key is to economize.

The loopy search function is an anti-pattern because at some (uncalculated) number of iterations, it will exceed the block gas limit and cease to work at all. Generally, design everything to complete with a consistent gas cost at any scale. Either you won't really need to put that in the contract because ancillary storage is expected to contain the extra details, or create internal mappings to solve for searches in one operation.

I noticed pragma solidity ^0.4.2; which is quite old. If the constant suggestion gives you trouble, it's probably because you're actually using a newer compiler, in which case try view.

This thread might give you some ideas. Are there well-solved and simple storage patterns for Solidity?

Hope it helps.

Here's the slightly modified contract in Remix to show it working:

enter image description here

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.