Files
Goa-gel-fullstack/blockchain/contracts/WorkflowRegistry.sol
Mahi 80566bf0a2 feat: Goa GEL Blockchain e-Licensing Platform - Full Stack Implementation
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
2026-02-07 10:23:29 -04:00

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;
}
}