Cover photo

Storing Hashes on the Blockchain

In Bitcoinu, to ensure the transparency of data and algorithms, they are publicly disclosed and their hashes(SHA-256) are stored on the blockchain (smart contract).

This guarantees that the data has not been arbitrarily modified after publication.

The following two main types of hashes are stored:

① Applicant List Hashes (applicantHashes)

A hashed, ordered list of all applicants for each 4-day block is stored in an array format.

② Other Hashes (otherHashes)

Hashes of various program codes are stored in a Key-Value format. The stored hashes include:

  • code_number_of_winners → Hash of the program code that determines the number of winners for both authenticated and unauthenticated users.

  • code_lottery_algorithm → Hash of the program code that selects winners based on the block hash and applicant list.

Smart Contract Code

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

/**
 * @title HashStorage
 * @dev Hash storage contract on Base Layer 2
 * Stores each hash element using a common structure.
 */
contract HashStorage {
    // Variables for access control
    address public admin;         // Administrator address (has permission to store and edit)
    address public superAdmin;    // Super administrator address (can change the administrator address)

    // Common structure definition for hash entries
    struct HashEntry {
        bytes32 hash;        // Hash value
        uint256 updated;     // Block number when updated
    }

    // Hashes of the applicant list (stored as an array of structures)
    HashEntry[] public applicantHashes;

    // Other hashes (stored in key-value format with values held in structures)
    mapping(bytes32 => HashEntry) public otherHashes;

    // Events
    event ApplicantHashAdded(bytes32 hash, uint256 index, uint256 blockNumber);
    event ApplicantHashUpdated(uint256 index, bytes32 oldHash, bytes32 newHash, uint256 blockNumber);
    event OtherHashUpdated(bytes32 indexed key, bytes32 hash, uint256 blockNumber);
    event AdminChanged(address oldAdmin, address newAdmin);
    event SuperAdminChanged(address oldSuperAdmin, address newSuperAdmin);

    /**
     * @dev Constructor
     * @param _admin Initial administrator address
     */
    constructor(address _admin) {
        require(_admin != address(0), "Invalid admin address");
        admin = _admin;
        superAdmin = msg.sender;  // The deployer becomes the super administrator
    }

    /**
     * @dev Modifier for functions that can only be executed by the administrator
     */
    modifier onlyAdmin() {
        require(msg.sender == admin, "Only admin can call this function");
        _;
    }

    /**
     * @dev Modifier for functions that can only be executed by the super administrator
     */
    modifier onlySuperAdmin() {
        require(msg.sender == superAdmin, "Only super admin can call this function");
        _;
    }

    // =========== Functions related to applicant hashes ===========

    /**
     * @dev Adds a new hash to the applicant hash list
     * @param _hash Hash to be added
     * @return The index of the added element
     */
    function addApplicantHash(bytes32 _hash) external onlyAdmin returns (uint256) {
        applicantHashes.push(HashEntry({
            hash: _hash,
            updated: block.number
        }));
        
        uint256 newIndex = applicantHashes.length - 1;
        
        emit ApplicantHashAdded(_hash, newIndex, block.number);
        
        return newIndex;
    }

    /**
     * @dev Updates the hash at a specific index in the applicant hash list
     * @param _index Index to be updated
     * @param _hash New hash value
     */
    function updateApplicantHash(uint256 _index, bytes32 _hash) external onlyAdmin {
        require(_index < applicantHashes.length, "Index out of bounds");
        
        bytes32 oldHash = applicantHashes[_index].hash;
        applicantHashes[_index].hash = _hash;
        applicantHashes[_index].updated = block.number;
        
        emit ApplicantHashUpdated(_index, oldHash, _hash, block.number);
    }

    /**
     * @dev Retrieves the hash at a specific index in the applicant hash list
     * @param _index Index to be retrieved
     * @return hash The hash value
     * @return updated The block number when it was last updated
     */
    function getApplicantHash(uint256 _index) external view returns (bytes32 hash, uint256 updated) {
        require(_index < applicantHashes.length, "Index out of bounds");
        
        HashEntry storage entry = applicantHashes[_index];
        return (entry.hash, entry.updated);
    }

    // =========== Functions related to other hashes ===========

    /**
     * @dev Sets other hashes (key-value format)
     * @param _key Key for the hash
     * @param _hash Hash value
     */
    function setOtherHash(bytes32 _key, bytes32 _hash) external onlyAdmin {
        otherHashes[_key] = HashEntry({
            hash: _hash,
            updated: block.number
        });
        
        emit OtherHashUpdated(_key, _hash, block.number);
    }

    /**
     * @dev Retrieves other hashes
     * @param _key Key for the hash
     * @return hash The hash value
     * @return updated The block number when it was last updated
     */
    function getOtherHash(bytes32 _key) external view returns (bytes32 hash