# 05 — Smart Contract: DocumentRegistry **File:** `blockchain/contracts/DocumentRegistry.sol` **Solidity version:** `^0.8.24` **Network:** Ethereum Sepolia Testnet (production) / Hardhat Local (development) **Purpose:** Tamper-proof, timestamped proof-of-deposit for BFSI contract documents. --- ## Design Philosophy > No document content is stored on-chain — only its SHA-256 hash. This design provides: - **Privacy**: No sensitive data ever reaches the blockchain - **Verifiability**: Anyone with the original document can verify it existed at a specific time - **Immutability**: Once registered, the hash and timestamp cannot be altered - **Legal Standing**: Block timestamp provides a cryptographically provable submission date --- ## How It Works ``` 1. User uploads contract PDF to LexiChain 2. Server computes SHA-256 hash of the raw file bytes 3. Server wallet signs and sends registerDocument(bytes32) transaction 4. Transaction is mined → block.timestamp becomes the proof date 5. txHash, blockNumber, blockTimestamp saved to PostgreSQL 6. User can verify anytime via verifyDocument(bytes32) ``` --- ## Contract Architecture ### State Variables | Variable | Type | Visibility | Description | |----------|------|-----------|-------------| | `owner` | `address` | `public` | Platform backend wallet address | | `totalDocuments` | `uint256` | `public` | Total number of registered document hashes | | `documents` | `mapping(bytes32 => DocumentRecord)` | `private` | Hash → registration record | | `depositorDocuments` | `mapping(address => bytes32[])` | `private` | Address → list of hashes they registered | ### Structs ```solidity struct DocumentRecord { uint256 timestamp; // block.timestamp at registration address depositor; // msg.sender (platform wallet) bool exists; // false = not registered } ``` ### Events ```solidity event DocumentRegistered( bytes32 indexed docHash, uint256 timestamp, address indexed depositor ); event DocumentVerified( bytes32 indexed docHash, bool exists, address indexed verifier ); ``` ### Modifiers ```solidity modifier onlyOwner() { require(msg.sender == owner, "Only owner can call this function"); _; } ``` --- ## Function Reference ### `registerDocument(bytes32 _docHash)` — write ``` Access: onlyOwner Gas: ~50,000 gas ``` Registers a document hash on-chain. Reverts if the same hash is already registered (idempotent via `requires`). **Parameters:** - `_docHash` — SHA-256 hash of the document as `bytes32` **Emits:** `DocumentRegistered(docHash, block.timestamp, msg.sender)` --- ### `verifyDocument(bytes32 _docHash)` — view (free) ``` Access: Anyone Gas: 0 (read-only call) ``` Checks if a document hash is registered and returns its record. **Returns:** ```solidity bool exists // true if registered uint256 timestamp // Unix timestamp of registration (0 if not found) address depositor // Address that registered it (address(0) if not found) ``` --- ### `getTimestamp(bytes32 _docHash)` — view (free) Returns only the registration timestamp for a given hash (0 if not registered). --- ### `getDocumentsByDepositor(address _depositor)` — view (free) Returns all document hashes registered by a specific address. --- ### `getDocumentCount(address _depositor)` — view (free) Returns the count of documents registered by a specific address. --- ### `transferOwnership(address _newOwner)` — write ``` Access: onlyOwner ``` Transfers contract ownership to a new address. Required if the platform wallet changes. --- ## ABI (Application Binary Interface) ```json [ "function registerDocument(bytes32 _docHash) external", "function verifyDocument(bytes32 _docHash) external view returns (bool exists, uint256 timestamp, address depositor)", "function getTimestamp(bytes32 _docHash) external view returns (uint256)", "function getDocumentsByDepositor(address _depositor) external view returns (bytes32[] memory)", "function getDocumentCount(address _depositor) external view returns (uint256)", "function totalDocuments() external view returns (uint256)", "function owner() external view returns (address)", "function transferOwnership(address _newOwner) external", "event DocumentRegistered(bytes32 indexed docHash, uint256 timestamp, address indexed depositor)" ] ``` --- ## Security Model | Property | Implementation | |----------|---------------| | **Owner-only writes** | `onlyOwner` modifier prevents anyone else from registering | | **No duplicates** | `require(!documents[hash].exists)` prevents double-registration | | **Privacy** | Only hashes stored — no filename, no metadata, no user data | | **Immutability** | No update/delete functions — hashes are permanent once registered | | **Dedicated wallet** | Platform uses a separate wallet (not a personal wallet) | --- ## Deployment ### Prerequisites 1. Install Hardhat dependencies: ```bash cd blockchain npm install ``` 2. Compile the contract: ```bash npx hardhat compile ``` --- ### Local Development (Hardhat Node) The `npm run dev` command in the root automatically: 1. Starts a Hardhat node on `http://localhost:8545` 2. Deploys `DocumentRegistry.sol` to the local chain 3. Saves the contract address to `blockchain/.dev-deploy.json` 4. Starts the Next.js dev server with the correct env vars To deploy manually: ```bash # Terminal 1: Start local node cd blockchain npx hardhat node # Terminal 2: Deploy npx hardhat run scripts/deploy.ts --network localhost ``` --- ### Production (Ethereum Sepolia Testnet) **Why Sepolia?** - 100% free (no real ETH needed) - Stable Ethereum testnet maintained by the Ethereum Foundation - Supported by Etherscan, Alchemy, Infura, MetaMask - No node to run or maintain - Transactions viewable on https://sepolia.etherscan.io **Step 1: Get Sepolia ETH (free)** Use one of these faucets: - https://sepoliafaucet.com (Alchemy — requires account) - https://faucet.quicknode.com/ethereum/sepolia - https://www.infura.io/faucet/sepolia A few test ETH is enough for thousands of contract registrations (~0.0005 ETH per registration). **Step 2: Set environment variables** ```bash # In blockchain/.env or your shell: SEPOLIA_RPC_URL=https://rpc.sepolia.org DEPLOYER_PRIVATE_KEY=0x ``` > ⚠️ **CRITICAL**: Never use a wallet that holds real ETH. Create a dedicated deployment wallet. **Step 3: Deploy to Sepolia** ```bash cd blockchain npx hardhat run scripts/deploy.ts --network sepolia ``` The script outputs: ``` DocumentRegistry deployed to: 0x BLOCKCHAIN_CONTRACT_ADDRESS=0x ``` **Step 4: Set app environment variables** ```bash BLOCKCHAIN_NETWORK=sepolia BLOCKCHAIN_RPC_URL=https://rpc.sepolia.org BLOCKCHAIN_CONTRACT_ADDRESS=0x BLOCKCHAIN_PRIVATE_KEY=0x ``` **Step 5: Verify deployment (optional)** Visit `https://sepolia.etherscan.io/address/` to confirm deployment. --- ## Running Tests ```bash cd blockchain npx hardhat test ``` Test file: `blockchain/test/DocumentRegistry.test.ts` --- ## Deployed Contract Information After deploying, record your contract address here for team reference: | Network | Contract Address | Deployer | Deployed At | |---------|-----------------|---------|-------------| | Sepolia | `0x...` | `0x...` | — | | Hardhat (dev) | Auto-deployed | Account #0 | Per session |