Smart Contract - bistecglobal/blockchain-certificates-issuer GitHub Wiki

Smart Contract Design

The smart contract forms the backbone of the blockchain certificate issuer application. It is deployed on the Ethereum blockchain and facilitates the issuance, verification, and sharing of certificates.

Smart Contract Structure

The smart contract is defined using Solidity, the programming language for Ethereum smart contracts. It consists of the following main components:

Structs: The User and Certificate structs define the structure of user data and certificate information, respectively. The UserType enum enumerates the different user roles.

contract Certification{
    struct User {
        string id;
        UserType userType;
        Certificate[] certificates;
        string[] sharedCertificates;
    }

    enum UserType { Issuer, Holder, Verifier , None }

    struct Certificate {
        string certificateId;
        string certificateDetail;
        address Issuer;
        bool verified;
    }

Mapping: The mapping data structure is used to associate Ethereum addresses with user data. This allows efficient storage and retrieval of user information.

mapping(address => User) private users;

Functions: The contract contains various functions that enable user registration, certificate issuance, verification, sharing, and retrieval.

Smart Contract Functions

registerUser

This function allows users to register with their Ethereum address, ID, and user type. It prevents duplicate registrations by checking if the user already exists.

function registerUser(address userAddress, string memory id , UserType userType) public {
        require(bytes(users[userAddress].id).length == 0, "User already exists");

        User storage user = users[userAddress];
        user.id = id;
        user.userType = userType;
    }

issueCertificate

Issuers utilize this function to issue certificates to users. If the user does not exist, the function first registers the user. The function records certificate details, the issuer's address, and the certificate ID.

function issueCertificate(address userAddress, string memory certificateId,string memory certificateDetail,string memory id , UserType userType ) public {
      
      if (bytes(users[userAddress].id).length == 0) {
        registerUser(userAddress, id, userType);
    }
  
        User storage user = users[userAddress];
        Certificate memory certificate;
        certificate.certificateId = certificateId;
        certificate.Issuer = msg.sender;
        certificate.certificateDetail = certificateDetail;
         user.certificates.push(certificate);

    
      
    }

verifyCertificate

Users and verifiers can call this function to verify if a given certificate ID is associated with a specific user. It returns true if the certificate is valid for the user.

   function verifyCertificate(address userAddress, string memory certificateId) public view returns (bool) {
         User storage user = users[userAddress];
        Certificate[] storage certificates = user.certificates;
        bool verifyed = false;
        for (uint i = 0; i < certificates.length; i++) {
            if (keccak256(bytes(certificates[i].certificateId)) == keccak256(bytes(certificateId))) {
                verifyed = true;
                 break;
            }
        }

        return verifyed;
    }

getUser

By providing an Ethereum address, users can retrieve their ID and user type from the contract. This function aids in displaying user information.

 function getUser(address userAddress) public view returns (string memory, UserType) {
        User storage user = users[userAddress];
        if (bytes(users[userAddress].id).length > 0){
        return (user.id, user.userType);
        }
        return ("0" ,UserType.None );
    }

getUserCertificates

This function allows users to retrieve a list of all certificates they own.

    function getUserCertificates(address userAddress) public view returns (Certificate[] memory) {
        if (bytes(users[userAddress].id).length == 0) {
        return new Certificate[](0);
    }

    User storage user = users[userAddress];
    return user.certificates;
    
    }

getUserCertificateById

Users can use this function to retrieve detailed information about a specific certificate they own, based on its certificate ID.

    function getUserCertificateById(address userAddress ,string memory certificateId) public view returns (Certificate[] memory) {
        if (bytes(users[userAddress].id).length == 0) {
        return new Certificate[](0);
    }

        User storage user = users[userAddress];
        Certificate[] storage certificates = user.certificates;
        Certificate[] memory result;

        for (uint i = 0; i < certificates.length; i++) {
            if (keccak256(bytes(certificates[i].certificateId)) == keccak256(bytes(certificateId))) {
                 result = new Certificate[](1);
            result[0] = certificates[i];
            return result;
            }
      
        }
        return new Certificate[](0);   
    }

shareCertificate

Users can share certificates by adding certificate IDs to their sharedCertificates array.

   function shareCertificate(string memory certificateId) public {

        User storage user = users[msg.sender];
        user.sharedCertificates.push(certificateId);
        
   
    }

checkSharedCertificate

This function checks whether a given certificate ID is present in a user's sharedCertificates array, indicating if the certificate has been shared.

    function checkSharedCertificate(string memory certificateId) public  view returns (bool) {
   
        User storage user = users[msg.sender];
        bool isShared = false;
        for (uint i = 0; i < user.sharedCertificates.length; i++) {
        if (keccak256(bytes(user.sharedCertificates[i])) == keccak256(bytes(certificateId)))  {
            isShared = true;
            break;
            }
        }
        return isShared;
    }

checkCertificateWithUser

Users can check if they own a specific certificate by providing its certificate ID.

   function checkCertificateWithUser(string memory certificateId) public  view returns (bool) {
       
             User storage user = users[msg.sender];
            Certificate[] storage certificates = user.certificates;
            bool isCorrect = false;

            for (uint i = 0; i < certificates.length; i++) {
            if (keccak256(bytes(certificates[i].certificateId)) == keccak256(bytes(certificateId))) {
                  isCorrect = true;
                break;
            }
            
           
        }
    return isCorrect;
    }

getSharedCertificates

Users can retrieve a list of certificate IDs that have been shared with them.

    function getSharedCertificates(address userAddress) public view returns (string[] memory) {
        User storage user = users[userAddress];
        return user.sharedCertificates;
    }

Data Storage and Security

  • The user data and certificate details are stored directly on the Ethereum blockchain, ensuring transparency and immutability.
  • The use of private mappings and local storage within the smart contract provides data privacy.
  • Certificates are linked to their issuers' Ethereum addresses, enhancing accountability.

Benefits of Smart Contract

  • Eliminates the need for intermediaries in certificate issuance and verification.
  • Enhances trust and transparency through immutable, blockchain-based records.
  • Facilitates secure sharing and verification of certificates, reducing fraud.