Final Push
This commit is contained in:
281
docs/05-smart-contract.md
Normal file
281
docs/05-smart-contract.md
Normal file
@@ -0,0 +1,281 @@
|
||||
# 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 |
|
||||
Reference in New Issue
Block a user