Developing a decentralized document verification and notarization system on celo

Developing a decentralized document verification and notarization system on celo https://celo.academy/uploads/default/optimized/2X/8/8dff133c4b3b26f8db6a66c38919d9bfe21340e0_2_1024x576.png
none 0.0 0

Introduction

In this tutorial, you will learn how to build a decentralized document verification and notarization system on the celo blockchain using Solidity.

Document verification and notarization are crucial processes in various domains such as legal, financial, and real estate, where ensuring the authenticity and integrity of documents is of utmost importance. By developing a decentralized system, we can leverage the transparency, immutability, and tamper-proof nature of the celo blockchain to provide a secure and reliable solution for document verification.

You can find the repository for this tutorial Here

Prerequisites

To follow this tutorial, you will need the following:

  • Basic understanding of Solidity and smart contracts.
  • A Development Environment Like Remix.
  • The Celo Extension Wallet.

Contract Developement

The complete code contract look like this

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract DocumentNotary {
    struct Document {
        string documentHash;
        bool isNotarized;
        address owner;
        mapping(address => bool) signers;
        uint256 signerCount;
    }

    mapping(string => Document) public documents;
    mapping(address => bool) public notaries;

    modifier onlyOwner(string memory _documentHash) {
        require(msg.sender == documents[_documentHash].owner, "Not the document owner");
        _;
    }

    modifier onlyNotary() {
        require(notaries[msg.sender], "Not a registered notary");
        _;
    }

    event DocumentAdded(string _documentHash, address indexed _owner);
    event DocumentNotarized(string _documentHash, address indexed _notary);
    event DocumentSigned(string _documentHash, address indexed _signer);

    constructor() {
        // The contract deployer is added as a notary by default
        notaries[msg.sender] = true;
    }

    function registerNotary(address _notary) public onlyNotary {
        notaries[_notary] = true;
    }

    function addDocument(string memory _documentHash) public {
        require(bytes(documents[_documentHash].documentHash).length == 0, "Document already exists");

        Document storage doc = documents[_documentHash];
        doc.documentHash = _documentHash;
        doc.isNotarized = false;
        doc.owner = msg.sender;

        emit DocumentAdded(_documentHash, msg.sender);
    }

    function notarizeDocument(string memory _documentHash) public onlyNotary {
        require(!documents[_documentHash].isNotarized, 'Document already notarized');

        documents[_documentHash].isNotarized = true;

        emit DocumentNotarized(_documentHash, msg.sender);
    }

    function signDocument(string memory _documentHash) public onlyOwner(_documentHash) {
        require(!documents[_documentHash].signers[msg.sender], 'Already signed the document');

        documents[_documentHash].signers[msg.sender] = true;
        documents[_documentHash].signerCount++;

        emit DocumentSigned(_documentHash, msg.sender);
    }

    function verifyDocument(string memory _documentHash, address _signer) public view returns(bool, uint256) {
        return (documents[_documentHash].signers[_signer], documents[_documentHash].signerCount);
    }
}

Code Breakdown

Let’s look at our smart contract in detail.

Structures and Variables

The contract begins by defining a structure Document which holds information about a document including its hash, notarization status, the owner of the document, a mapping of signers, and the total number of signers.

struct Document {
    string documentHash;
    bool isNotarized;
    address owner;
    mapping(address => bool) signers;
    uint256 signerCount;
}

There are two key mappings that are used to store all the documents and notaries in the contract.

mapping(string => Document) public documents;
mapping(address => bool) public notaries;

Modifiers

There are two modifier functions onlyOwner and onlyNotary . Modifiers are used in Solidity to change the behavior of functions in a declarative way. The onlyOwner modifier ensures that a function can only be called by the owner of a document, and the onlyNotary modifier ensures that a function can only be called by a registered notary.

modifier onlyOwner(string memory _documentHash) {
    require(msg.sender == documents[_documentHash].owner, "Not the document owner");
    _;
}

modifier onlyNotary() {
    require(notaries[msg.sender], "Not a registered notary");
    _;
}

Events

Three events are defined: DocumentAdded , DocumentNotarized , and DocumentSigned . These events provide a way for external consumers to track activities occurring inside the contract.

event DocumentAdded(string _documentHash, address indexed _owner);
event DocumentNotarized(string _documentHash, address indexed _notary);
event DocumentSigned(string _documentHash, address indexed _signer);

Constructor

The constructor is a special function that is executed when the contract is deployed. In this contract, the constructor assigns the contract deployer as the initial notary.

constructor() {
    notaries[msg.sender] = true;
}

Register Notary Function

The registerNotary function allows an existing notary to add a new notary.

function registerNotary(address _notary) public onlyNotary {
    notaries[_notary] = true;
}

Add Document Function

The addDocument function allows a user to add a new document by its hash. It checks if the document already exists before creating a new one.

function addDocument(string memory _documentHash) public {
    require(bytes(documents[_documentHash].documentHash).length == 0, "Document already exists");

    Document storage doc = documents[_documentHash];
    doc.documentHash = _documentHash;
    doc.isNotarized = false;
    doc.owner = msg.sender;

    emit DocumentAdded(_documentHash, msg.sender);
}

Notarize Document Function

The notarizeDocument function updates the notarization status of a document and emits the DocumentNotarized event. This function can only be called by a registered notary and only on documents that haven’t already been notarized.

function notarizeDocument(string memory _documentHash) public onlyNotary {
    require(!documents[_documentHash].isNotarized, 'Document already notarized');

    documents[_documentHash].isNotarized = true;
    emit DocumentNotarized(_documentHash, msg.sender);
}

Sign Document Function

The signDocument function allows the owner of a document to sign it. It checks whether the document has already been signed by the owner, and if not, it records the signature and increments the count of signers. Then it emits the DocumentSigned event.

function signDocument(string memory _documentHash) public onlyOwner(_documentHash) {
    require(!documents[_documentHash].signers[msg.sender], 'Already signed the document');

    documents[_documentHash].signers[msg.sender] = true;
    documents[_documentHash].signerCount++;

    emit DocumentSigned(_documentHash, msg.sender);
}

Verify Document Function

The verifyDocument function allows anyone to verify whether a specific address has signed a specific document. This is achieved by checking the signers mapping of the document. It returns two values: whether the queried address has signed the document, and the total number of signers of the document.

function verifyDocument(string memory _documentHash, address _signer) public view returns(bool, uint256) {
    return (documents[_documentHash].signers[_signer], documents[_documentHash].signerCount);
}

Deployment

To deploy our smart contract successfully, we need the celo extention wallet which can be downloaded from here

Next, we need to fund our newly created wallet which can done using the celo alfojares faucet Here

Now, click on the plugin logo at the bottom left corner and search for celo plugin.

Install the plugin and click on the celo logo which will show in the side tab after the plugin is installed.

Next connect your Celo wallet, select the contract you want to deploy and finally click on deploy to deploy your contract.

Conclusion

In this tutorial, we explained how to create a decentralized document notarization and verification system on the Celo blockchain using Solidity. The system allows users to upload document hashes, notaries to notarize them, and anyone to verify whether a user has signed a specific document. The contract, DocumentNotary , uses various Solidity concepts including structs, mappings, modifiers, and events. The system provides a transparent, immutable mechanism for notarizing and verifying documents.

Next Steps

I hope you learned a lot from this tutorial. Here are some relevant links that would aid your learning further.

About the author

I’m Jonathan Iheme, A full stack block-chain Developer from Nigeria.

linkedIn
Twitter

2 Likes

Congratulations on being among the top voted proposals this week! This is now approved by @Celo_Academy for you to get started whenever you’d like. :mortar_board: :seedling:

2 Likes

@4undRaiser i will review this in 1 - 2 days

1 Like

@4undRaiser your tutorial looks great you can move it to the next column.

2 Likes