Files
Goa-gel-fullstack/blockchain/contracts/DocumentChain.sol

194 lines
5.4 KiB
Solidity
Raw Normal View History

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/access/Ownable.sol";
/**
* @title DocumentChain
* @notice Records and verifies document hashes on the blockchain
* @dev Provides tamper-proof document verification
*/
contract DocumentChain is Ownable {
struct DocumentRecord {
bytes32 id;
string requestId;
string documentId;
string hash;
uint256 version;
uint256 timestamp;
address uploadedBy;
}
// Mapping from document ID to its records
mapping(string => DocumentRecord[]) private _documentHistory;
// Mapping from document ID to latest hash
mapping(string => string) private _latestHashes;
// Mapping from hash to document ID (for reverse lookup)
mapping(string => string) private _hashToDocument;
// Counter for unique record IDs
uint256 private _recordCounter;
// Events
event DocumentRecorded(
bytes32 indexed recordId,
string indexed requestId,
string documentId,
string hash,
uint256 version,
uint256 timestamp
);
constructor() Ownable(msg.sender) {}
/**
* @notice Record a document hash
* @param requestId The license request ID
* @param documentId The document ID
* @param hash The document hash (e.g., SHA-256)
* @param version The document version
* @return recordId The unique record ID
*/
function recordDocumentHash(
string calldata requestId,
string calldata documentId,
string calldata hash,
uint256 version
) public onlyOwner returns (bytes32) {
require(bytes(requestId).length > 0, "Request ID required");
require(bytes(documentId).length > 0, "Document ID required");
require(bytes(hash).length > 0, "Hash required");
_recordCounter++;
bytes32 recordId = keccak256(
abi.encodePacked(documentId, version, block.timestamp, _recordCounter)
);
DocumentRecord memory record = DocumentRecord({
id: recordId,
requestId: requestId,
documentId: documentId,
hash: hash,
version: version,
timestamp: block.timestamp,
uploadedBy: msg.sender
});
_documentHistory[documentId].push(record);
_latestHashes[documentId] = hash;
_hashToDocument[hash] = documentId;
emit DocumentRecorded(
recordId,
requestId,
documentId,
hash,
version,
block.timestamp
);
return recordId;
}
/**
* @notice Verify if a document hash exists
* @param documentId The document ID
* @param hash The hash to verify
* @return True if the hash matches any recorded version
*/
function verifyDocumentHash(string calldata documentId, string calldata hash)
public
view
returns (bool)
{
DocumentRecord[] memory history = _documentHistory[documentId];
for (uint256 i = 0; i < history.length; i++) {
if (keccak256(bytes(history[i].hash)) == keccak256(bytes(hash))) {
return true;
}
}
return false;
}
/**
* @notice Verify if a hash is the latest version
* @param documentId The document ID
* @param hash The hash to verify
* @return True if the hash is the latest version
*/
function verifyLatestHash(string calldata documentId, string calldata hash)
public
view
returns (bool)
{
return keccak256(bytes(_latestHashes[documentId])) == keccak256(bytes(hash));
}
/**
* @notice Get the complete history of a document
* @param documentId The document ID
* @return Array of DocumentRecord structs
*/
function getDocumentHistory(string calldata documentId)
public
view
returns (DocumentRecord[] memory)
{
return _documentHistory[documentId];
}
/**
* @notice Get the latest hash for a document
* @param documentId The document ID
* @return The latest document hash
*/
function getLatestDocumentHash(string calldata documentId)
public
view
returns (string memory)
{
return _latestHashes[documentId];
}
/**
* @notice Get document ID by hash
* @param hash The document hash
* @return The document ID
*/
function getDocumentByHash(string calldata hash)
public
view
returns (string memory)
{
return _hashToDocument[hash];
}
/**
* @notice Get the version count for a document
* @param documentId The document ID
* @return The number of versions
*/
function getVersionCount(string calldata documentId) public view returns (uint256) {
return _documentHistory[documentId].length;
}
/**
* @notice Get a specific version of a document
* @param documentId The document ID
* @param version The version number (1-indexed)
* @return The DocumentRecord for that version
*/
function getDocumentVersion(string calldata documentId, uint256 version)
public
view
returns (DocumentRecord memory)
{
require(version > 0 && version <= _documentHistory[documentId].length, "Invalid version");
return _documentHistory[documentId][version - 1];
}
}