Blockchain-Based Identity Verification Solidity, JavaScript
👤 Sharing: AI
```javascript
// Solidity Smart Contract (Identity.sol)
pragma solidity ^0.8.0;
contract Identity {
struct IdentityData {
string name;
string documentHash; // Hash of the user's identifying document (e.g., passport, driver's license)
uint256 timestamp; // Timestamp of when the identity was registered
bool verified;
}
mapping(address => IdentityData) public identities;
address public owner; // Address of the contract owner
event IdentityRegistered(address indexed userAddress, string name, string documentHash);
event IdentityVerified(address indexed userAddress);
event IdentityDataUpdated(address indexed userAddress, string newName, string newDocumentHash);
constructor() {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner, "Only the contract owner can call this function.");
_;
}
// Registers a new identity
function registerIdentity(string memory _name, string memory _documentHash) public {
require(bytes(_name).length > 0, "Name cannot be empty.");
require(bytes(_documentHash).length > 0, "Document Hash cannot be empty.");
require(identities[msg.sender].timestamp == 0, "Identity already registered for this address."); // Check if the identity is already registered
identities[msg.sender] = IdentityData(_name, _documentHash, block.timestamp, false);
emit IdentityRegistered(msg.sender, _name, _documentHash);
}
// Verifies an identity (only callable by the contract owner)
function verifyIdentity(address _userAddress) public onlyOwner {
require(identities[_userAddress].timestamp > 0, "Identity not registered for this address.");
require(!identities[_userAddress].verified, "Identity already verified for this address.");
identities[_userAddress].verified = true;
emit IdentityVerified(_userAddress);
}
// Allows a user to update their registered identity data (name and document hash)
function updateIdentityData(string memory _newName, string memory _newDocumentHash) public {
require(identities[msg.sender].timestamp > 0, "Identity not registered for this address.");
require(bytes(_newName).length > 0, "Name cannot be empty.");
require(bytes(_newDocumentHash).length > 0, "Document Hash cannot be empty.");
identities[msg.sender].name = _newName;
identities[msg.sender].documentHash = _newDocumentHash;
emit IdentityDataUpdated(msg.sender, _newName, _newDocumentHash);
}
// Returns whether an identity has been verified.
function isVerified(address _userAddress) public view returns (bool) {
return identities[_userAddress].verified;
}
// Returns the identity data of a user. If the identity isn't registered, it returns default values.
function getIdentityData(address _userAddress) public view returns (string memory, string memory, uint256, bool) {
IdentityData memory data = identities[_userAddress];
return (data.name, data.documentHash, data.timestamp, data.verified);
}
}
// JavaScript (interacting with the contract)
// Requires web3.js library and Metamask (or similar)
// Assuming web3 is initialized correctly (e.g., using Metamask)
// const web3 = new Web3(window.ethereum); // For Metamask
async function registerIdentity(contract, name, documentHash, account) {
try {
const tx = await contract.methods.registerIdentity(name, documentHash).send({ from: account });
console.log("Identity registered! Transaction hash:", tx.transactionHash);
} catch (error) {
console.error("Error registering identity:", error);
}
}
async function verifyIdentity(contract, userAddress, account) {
try {
const tx = await contract.methods.verifyIdentity(userAddress).send({ from: account });
console.log("Identity verified! Transaction hash:", tx.transactionHash);
} catch (error) {
console.error("Error verifying identity:", error);
}
}
async function updateIdentityData(contract, newName, newDocumentHash, account) {
try {
const tx = await contract.methods.updateIdentityData(newName, newDocumentHash).send({ from: account });
console.log("Identity data updated! Transaction hash:", tx.transactionHash);
} catch (error) {
console.error("Error updating identity data:", error);
}
}
async function isVerified(contract, userAddress) {
try {
const verified = await contract.methods.isVerified(userAddress).call();
console.log("Is Verified:", verified);
return verified;
} catch (error) {
console.error("Error checking verification status:", error);
return false; // Or handle the error as needed.
}
}
async function getIdentityData(contract, userAddress) {
try {
const identityData = await contract.methods.getIdentityData(userAddress).call();
console.log("Identity Data:", identityData);
return identityData; // Returns array: [name, documentHash, timestamp, verified]
} catch (error) {
console.error("Error getting identity data:", error);
return null; // Or handle the error as needed.
}
}
// Example usage (assuming you have the contract ABI and address):
// Contract ABI (put the ABI of the deployed contract here. Get it from Remix or your deployment output)
// const contractABI = [...]
// Contract Address (put the address of the deployed contract here)
// const contractAddress = '0x...';
// Example function to interact with the contract when a button is clicked
async function interactWithContract() {
const web3 = new Web3(window.ethereum);
await window.ethereum.enable(); //Request access to the user's accounts. Required for Metamask.
const accounts = await web3.eth.getAccounts();
const account = accounts[0]; // Use the first available account. In a real application, allow the user to select.
const contract = new web3.eth.Contract(contractABI, contractAddress);
// Example usage:
const userName = "Alice Smith";
const documentHash = "0x1234567890abcdef...";
await registerIdentity(contract, userName, documentHash, account); // Register the identity
let identityData = await getIdentityData(contract, account);
console.log("Identity data after registration:", identityData);
// Assuming the contract owner's account is different
const contractOwnerAccount = accounts[1]; // Use the second account as the owner (for testing)
await verifyIdentity(contract, account, contractOwnerAccount); // Verify the identity (only owner can call)
let isUserVerified = await isVerified(contract, account);
console.log("Is Verified?", isUserVerified); // Output: true
const newName = "Alice Jones";
const newDocumentHash = "0xfedcba0987654321...";
await updateIdentityData(contract, newName, newDocumentHash, account);
identityData = await getIdentityData(contract, account);
console.log("Identity data after update:", identityData);
}
```
Key improvements and explanations:
* **Solidity Code Improvements:**
* **`constructor()`:** Sets the `owner` of the contract. Critically important for `onlyOwner` modifier to work.
* **`onlyOwner` Modifier:** A modifier that restricts a function's execution to only the contract owner. This is *essential* for the `verifyIdentity` function.
* **`registerIdentity` function:** Includes `require` statements to prevent empty names and document hashes. Most importantly, it now checks if an identity is *already* registered for a given address using `identities[msg.sender].timestamp == 0`. This prevents re-registration.
* **`verifyIdentity` function:** Now also checks if the identity has *already* been verified before attempting to verify again. Prevents redundant transactions.
* **`updateIdentityData` Function:** Allows a user to update their name and document hash. Crucially, it also includes `require` statements to prevent empty values. It checks if the identity is registered before allowing an update.
* **`getIdentityData` function:** This is *essential* for retrieving the identity information in your JavaScript code. It returns all the relevant data (name, hash, timestamp, verification status).
* **`isVerified` function:** A simple function to quickly check if an identity is verified.
* **Events:** `IdentityRegistered`, `IdentityVerified`, and `IdentityDataUpdated` events are emitted after each significant action. This is crucial for off-chain monitoring and indexing of identity data. Use these events in your Javascript code to know when the transactions were mined.
* **Error Handling with `require`:** The `require` statements ensure that the contract behaves predictably and prevents common errors. The error messages provided in `require` are very important for debugging and understanding why a transaction might fail.
* **Timestamp:** The `timestamp` is now stored when the identity is registered. This can be useful for auditing or other time-sensitive operations.
* **Security Considerations:** While this example provides a basic framework, remember that storing sensitive data (like full documents or PII) directly on the blockchain is generally *not* recommended. The blockchain is public and immutable. Instead, store a *hash* of the document or PII. Also consider using more advanced identity management techniques if you need a higher level of security and privacy.
* **JavaScript Code Improvements:**
* **`web3` Initialization:** Explicitly states that `web3` should be initialized correctly (e.g., using Metamask).
* **`await window.ethereum.enable()`**: The essential Metamask permission request that *must* be done before any transactions. This is now standard and necessary for browser-based web3 interaction.
* **`getAccounts()` and Account Selection:** Shows how to get the user's accounts and select one to use for transactions. Important: In a real app, allow the user to choose their account, don't just assume the first one.
* **Error Handling:** Includes `try...catch` blocks in the JavaScript functions to handle potential errors during contract interaction. Logs the errors to the console.
* **Clearer Logging:** Uses `console.log` to print transaction hashes and identity data for better debugging.
* **`isVerified` Function:** Now retrieves and returns the `verified` status of an identity.
* **`getIdentityData` Function:** Fetches *all* the identity data and returns it to the caller.
* **Example Usage:** Provides a complete example of how to use all the functions: registering, verifying, updating, and querying data. Shows how to get data *after* each transaction, which is crucial for understanding how the blockchain state changes.
* **Contract ABI and Address:** Emphasizes the importance of replacing placeholders with the actual contract ABI and address.
* **Asynchronous Functions:** All functions that interact with the blockchain are now `async` and use `await`. This is essential for handling the asynchronous nature of blockchain interactions.
* **Using `call()` vs `send()`:** Uses `call()` for read-only functions (`isVerified`, `getIdentityData`) and `send()` for functions that modify the blockchain state (`registerIdentity`, `verifyIdentity`, `updateIdentityData`). `send()` requires an account (`from`) and incurs gas costs.
* **Comments:** Added more comments to explain the purpose of each code section.
* **Event Listening (Not Implemented - But Essential for Real Apps):** To make your application reactive, you would *listen* for the `IdentityRegistered`, `IdentityVerified`, and `IdentityDataUpdated` events emitted by the contract. This would allow you to update your UI automatically when an identity is registered or verified. See the "Next Steps" section below for how to do this.
* **Security and Best Practices:**
* **Document Hash:** Reinforces the importance of storing only the *hash* of the document, not the document itself. This protects user privacy.
* **Access Control:** The `onlyOwner` modifier protects the `verifyIdentity` function, preventing unauthorized users from verifying identities.
* **Input Validation:** The `require` statements in the Solidity code provide input validation to prevent common errors and vulnerabilities.
* **Error Handling:** The `try...catch` blocks in the JavaScript code provide error handling to gracefully handle potential errors during contract interaction.
* **Deployment and Setup (Important Considerations):**
* **Ganache/Hardhat:** You'll need a local blockchain environment like Ganache or Hardhat to deploy and test this contract.
* **Metamask:** Metamask is essential for interacting with the contract from your browser.
* **Contract ABI:** You need to get the contract ABI after compiling your Solidity code. This ABI tells web3.js how to interact with the contract. Remix IDE is an easy way to compile Solidity and get the ABI.
* **Contract Address:** After deploying the contract to your chosen blockchain (Ganache, Hardhat, or a testnet like Rinkeby), you'll get a contract address. You need this address to tell web3.js where the contract is located.
**How to Use This Code:**
1. **Set up your environment:**
* Install Node.js and npm.
* Install Ganache or Hardhat.
* Install Metamask.
2. **Compile and Deploy the Solidity Contract:**
* Use Remix IDE (browser-based) or Hardhat to compile the `Identity.sol` contract.
* Deploy the contract to your Ganache or Hardhat local blockchain.
* Note the contract address and ABI.
3. **Create an HTML file:**
```html
<!DOCTYPE html>
<html>
<head>
<title>Blockchain Identity Verification</title>
<script src="https://cdn.jsdelivr.net/npm/web3@1.7.0/dist/web3.min.js"></script>
</head>
<body>
<h1>Identity Verification</h1>
<button onclick="interactWithContract()">Interact with Contract</button>
<script>
// Paste the JavaScript code from above here.
// Remember to replace contractABI and contractAddress
// ... (JavaScript code) ...
</script>
</body>
</html>
```
4. **Run your code:**
* Open the HTML file in your browser.
* Metamask will prompt you to connect to your Ganache/Hardhat network.
* Click the "Interact with Contract" button.
* Metamask will prompt you to sign the transactions.
**Next Steps (Important for Real-World Applications):**
* **User Interface (UI):** Build a proper UI for users to enter their name, upload documents, and view their verification status.
* **Document Hashing:** Implement a secure way to hash documents on the client-side *before* sending the hash to the blockchain. Use a library like `js-sha256` in your JavaScript code.
* **Event Listening:** Instead of manually calling `getIdentityData` and `isVerified` after each transaction, *listen* for the `IdentityRegistered` and `IdentityVerified` events. This makes your UI reactive. Example:
```javascript
contract.events.IdentityRegistered({
fromBlock: 'latest' // Start listening from the latest block
}, function(error, event){
if (error) {
console.log(error);
} else {
console.log("Identity Registered Event:", event);
// Update your UI here
// Call getIdentityData to update the display
}
});
contract.events.IdentityVerified({
fromBlock: 'latest'
}, function(error, event){
if (error) {
console.log(error);
} else {
console.log("Identity Verified Event:", event);
// Update your UI here. For example, show a "Verified" badge.
// Call getIdentityData to update the display
}
});
contract.events.IdentityDataUpdated({
fromBlock: 'latest'
}, function(error, event){
if (error) {
console.log(error);
} else {
console.log("Identity Updated Event:", event);
}
});
```
This code snippet subscribes to the `IdentityRegistered` and `IdentityVerified` events and logs them to the console. **Most importantly**, it shows where you would update your UI in response to these events.
* **Off-Chain Storage (IPFS/Cloud Storage):** Consider storing the *actual* documents in a decentralized storage system like IPFS or a cloud storage service (AWS S3, Google Cloud Storage) and only store the hash of the document on the blockchain. This is crucial for scalability and privacy.
* **KYC/AML Compliance:** If you're building a real-world identity verification system, you'll need to comply with KYC (Know Your Customer) and AML (Anti-Money Laundering) regulations. This typically involves integrating with a KYC/AML provider.
* **Security Audits:** Before deploying a smart contract to a production environment, it's *essential* to have it audited by a reputable security firm.
* **Gas Optimization:** Optimize your Solidity code to reduce gas costs. This is important for making your application more affordable to use.
* **Testing:** Write thorough unit tests and integration tests to ensure that your smart contract works correctly.
This improved example provides a more complete and realistic foundation for building a blockchain-based identity verification system. Remember to prioritize security, privacy, and compliance in your implementation.
👁️ Viewed: 9
Comments