Complete implementation of the Goa Government e-Licensing platform with: Backend: - NestJS API with JWT authentication - PostgreSQL database with Knex ORM - Redis caching and session management - MinIO document storage - Hyperledger Besu blockchain integration - Multi-department workflow system - Comprehensive API tests (266/282 passing) Frontend: - Angular 21 with standalone components - Angular Material + TailwindCSS UI - Visual workflow builder - Document upload with progress tracking - Blockchain explorer integration - Role-based dashboards (Admin, Department, Citizen) - E2E tests with Playwright (37 tests) Infrastructure: - Docker Compose orchestration - Blockscout blockchain explorer - Development and production configurations
189 lines
5.6 KiB
Solidity
189 lines
5.6 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity ^0.8.20;
|
|
|
|
import "@openzeppelin/contracts/access/Ownable.sol";
|
|
|
|
/**
|
|
* @title WorkflowRegistry
|
|
* @notice Registers and tracks workflow definitions on-chain
|
|
* @dev Placeholder for future workflow verification capabilities
|
|
*/
|
|
contract WorkflowRegistry is Ownable {
|
|
struct WorkflowDefinition {
|
|
bytes32 id;
|
|
string workflowType;
|
|
string name;
|
|
bytes32 definitionHash;
|
|
uint256 version;
|
|
uint256 timestamp;
|
|
bool isActive;
|
|
}
|
|
|
|
// Mapping from workflow ID to definition
|
|
mapping(bytes32 => WorkflowDefinition) private _workflows;
|
|
|
|
// Mapping from workflow type to latest workflow ID
|
|
mapping(string => bytes32) private _latestWorkflows;
|
|
|
|
// Array of all workflow IDs
|
|
bytes32[] private _workflowIds;
|
|
|
|
// Counter for unique IDs
|
|
uint256 private _workflowCounter;
|
|
|
|
// Events
|
|
event WorkflowRegistered(
|
|
bytes32 indexed workflowId,
|
|
string indexed workflowType,
|
|
string name,
|
|
bytes32 definitionHash,
|
|
uint256 version
|
|
);
|
|
|
|
event WorkflowDeactivated(bytes32 indexed workflowId);
|
|
|
|
event WorkflowActivated(bytes32 indexed workflowId);
|
|
|
|
constructor() Ownable(msg.sender) {}
|
|
|
|
/**
|
|
* @notice Register a new workflow definition
|
|
* @param workflowType The type of workflow (e.g., "RESORT_LICENSE")
|
|
* @param name Human-readable name
|
|
* @param definitionHash Hash of the workflow definition JSON
|
|
* @return workflowId The unique workflow ID
|
|
*/
|
|
function registerWorkflow(
|
|
string calldata workflowType,
|
|
string calldata name,
|
|
bytes32 definitionHash
|
|
) public onlyOwner returns (bytes32) {
|
|
require(bytes(workflowType).length > 0, "Workflow type required");
|
|
require(bytes(name).length > 0, "Name required");
|
|
require(definitionHash != bytes32(0), "Definition hash required");
|
|
|
|
_workflowCounter++;
|
|
bytes32 workflowId = keccak256(
|
|
abi.encodePacked(workflowType, _workflowCounter, block.timestamp)
|
|
);
|
|
|
|
// Determine version
|
|
uint256 version = 1;
|
|
bytes32 latestId = _latestWorkflows[workflowType];
|
|
if (latestId != bytes32(0)) {
|
|
version = _workflows[latestId].version + 1;
|
|
// Deactivate previous version
|
|
_workflows[latestId].isActive = false;
|
|
}
|
|
|
|
WorkflowDefinition storage workflow = _workflows[workflowId];
|
|
workflow.id = workflowId;
|
|
workflow.workflowType = workflowType;
|
|
workflow.name = name;
|
|
workflow.definitionHash = definitionHash;
|
|
workflow.version = version;
|
|
workflow.timestamp = block.timestamp;
|
|
workflow.isActive = true;
|
|
|
|
_latestWorkflows[workflowType] = workflowId;
|
|
_workflowIds.push(workflowId);
|
|
|
|
emit WorkflowRegistered(
|
|
workflowId,
|
|
workflowType,
|
|
name,
|
|
definitionHash,
|
|
version
|
|
);
|
|
|
|
return workflowId;
|
|
}
|
|
|
|
/**
|
|
* @notice Get workflow definition by ID
|
|
* @param workflowId The workflow ID
|
|
* @return The WorkflowDefinition struct
|
|
*/
|
|
function getWorkflow(bytes32 workflowId)
|
|
public
|
|
view
|
|
returns (WorkflowDefinition memory)
|
|
{
|
|
require(_workflows[workflowId].timestamp > 0, "Workflow not found");
|
|
return _workflows[workflowId];
|
|
}
|
|
|
|
/**
|
|
* @notice Get the latest active workflow for a type
|
|
* @param workflowType The workflow type
|
|
* @return The WorkflowDefinition struct
|
|
*/
|
|
function getLatestWorkflow(string calldata workflowType)
|
|
public
|
|
view
|
|
returns (WorkflowDefinition memory)
|
|
{
|
|
bytes32 workflowId = _latestWorkflows[workflowType];
|
|
require(workflowId != bytes32(0), "No workflow found for type");
|
|
return _workflows[workflowId];
|
|
}
|
|
|
|
/**
|
|
* @notice Verify a workflow definition hash
|
|
* @param workflowId The workflow ID
|
|
* @param definitionHash The hash to verify
|
|
* @return True if the hash matches
|
|
*/
|
|
function verifyWorkflow(bytes32 workflowId, bytes32 definitionHash)
|
|
public
|
|
view
|
|
returns (bool)
|
|
{
|
|
WorkflowDefinition memory workflow = _workflows[workflowId];
|
|
return workflow.isActive && workflow.definitionHash == definitionHash;
|
|
}
|
|
|
|
/**
|
|
* @notice Deactivate a workflow
|
|
* @param workflowId The workflow ID
|
|
*/
|
|
function deactivateWorkflow(bytes32 workflowId) public onlyOwner {
|
|
require(_workflows[workflowId].timestamp > 0, "Workflow not found");
|
|
require(_workflows[workflowId].isActive, "Workflow already inactive");
|
|
|
|
_workflows[workflowId].isActive = false;
|
|
|
|
emit WorkflowDeactivated(workflowId);
|
|
}
|
|
|
|
/**
|
|
* @notice Activate a workflow
|
|
* @param workflowId The workflow ID
|
|
*/
|
|
function activateWorkflow(bytes32 workflowId) public onlyOwner {
|
|
require(_workflows[workflowId].timestamp > 0, "Workflow not found");
|
|
require(!_workflows[workflowId].isActive, "Workflow already active");
|
|
|
|
_workflows[workflowId].isActive = true;
|
|
|
|
emit WorkflowActivated(workflowId);
|
|
}
|
|
|
|
/**
|
|
* @notice Get total workflow count
|
|
* @return The number of registered workflows
|
|
*/
|
|
function getWorkflowCount() public view returns (uint256) {
|
|
return _workflowIds.length;
|
|
}
|
|
|
|
/**
|
|
* @notice Check if a workflow is active
|
|
* @param workflowId The workflow ID
|
|
* @return True if active
|
|
*/
|
|
function isWorkflowActive(bytes32 workflowId) public view returns (bool) {
|
|
return _workflows[workflowId].isActive;
|
|
}
|
|
}
|