282 lines
7.4 KiB
Markdown
282 lines
7.4 KiB
Markdown
# 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<your-dedicated-wallet-private-key>
|
|
```
|
|
|
|
> ⚠️ **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<CONTRACT_ADDRESS>
|
|
BLOCKCHAIN_CONTRACT_ADDRESS=0x<CONTRACT_ADDRESS>
|
|
```
|
|
|
|
**Step 4: Set app environment variables**
|
|
|
|
```bash
|
|
BLOCKCHAIN_NETWORK=sepolia
|
|
BLOCKCHAIN_RPC_URL=https://rpc.sepolia.org
|
|
BLOCKCHAIN_CONTRACT_ADDRESS=0x<your-deployed-address>
|
|
BLOCKCHAIN_PRIVATE_KEY=0x<your-wallet-private-key>
|
|
```
|
|
|
|
**Step 5: Verify deployment (optional)**
|
|
|
|
Visit `https://sepolia.etherscan.io/address/<CONTRACT_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 |
|