//SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
import "hardhat/console.sol";
contract SharedStructs {
struct Service {
address actorID;
string serviceName;
string servicePurpose;
string operation;
string[] personalData;
}
}
contract DataUsage is SharedStructs {
mapping(address => Service[]) public services;
address[] public actorAddresses;
function addService(string memory _serviceName, string memory _servicePurpose, string memory _operation, string[] memory _personalData) public {
Service memory newService = Service(msg.sender, _serviceName, _servicePurpose, _operation, _personalData);
services[msg.sender].push(newService);
bool actorExists = false;
for (uint256 i = 0; i < actorAddresses.length; i++) {
if (actorAddresses[i] == msg.sender) {
actorExists = true;
break;
}
}
if (!actorExists) {
actorAddresses.push(msg.sender);
}
}
function getService(address _actorID, uint256 index) public view returns (Service memory) {
require(index < services[_actorID].length, "Index out of bounds");
return services[_actorID][index];
}
function getServicesCount(address _actorID) public view returns (uint256) {
return services[_actorID].length;
}
function getActorAddressesCount() public view returns (uint256) {
return actorAddresses.length;
}
}
// 'Banking Transactions', 'Banking Services', 'Write', ['Account Number', 'Transaction History'] 0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db
// 'Financial Transactions', 'Financial Services', 'Read', ['Phone Number', 'Account History'] 0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db
// 'Medical Transactions', 'Financial Services', 'Transfer', ['Phone Number', 'Account History'] 0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB
//0xc5075f64488d32a067f76bd1d177b5376e72e8a9e3f0fb2395c1014b928d0181, 0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db, 'Write', ['Account Number', 'Transaction History'], true //0x5B38Da6a701c568545dCfcB03FcB875f56beddC4
//0xc5075f64488d32a067f76bd1d177b5376e72e8a9e3f0fb2395c1014b928d0181, 0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB, 'Read', ['Phone Number', 'Account History'], true
//0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db, 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4, 'Write', ['Account Number', 'Transaction History'], 'Banking Services'
//0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB, 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4, 'Transfer', ['Phone Number', 'Account History'], 'Medical Services'
contract Agreement is SharedStructs {
struct Consent {
bytes32 purposeHash;
address actorID;
string operation;
string[] personalData;
address userId;
bool isPositive;
}
event LogServiceParams(
address indexed actorID,
string serviceName,
string servicePurpose,
string operation,
string[] personalData
);
mapping(address => mapping(address => Consent[])) public consents;
function giveConsent(bytes32 _purposeHash, address _actorID, string memory _operation, string[] memory _personalData, bool _isPositive) public {
Consent memory newConsent = Consent(_purposeHash, _actorID, _operation, _personalData, msg.sender, _isPositive);
consents[_actorID][msg.sender].push(newConsent);
}
function getConsents(address actorID, address userId) public view returns (Consent[] memory) {
return consents[actorID][userId];
}
function callGetService(address dataUsageContractAddress) public {
DataUsage dataUsage = DataUsage(dataUsageContractAddress);
uint256 numActors = dataUsage.getActorAddressesCount();
for (uint256 i = 0; i < numActors; i++) {
address actorAddress = dataUsage.actorAddresses(i);
uint256 numServices = dataUsage.getServicesCount(actorAddress);
for (uint256 j = 0; j < numServices; j++) {
Service memory service = dataUsage.getService(actorAddress, j);
emit LogServiceParams(
service.actorID,
service.serviceName,
service.servicePurpose,
service.operation,
service.personalData
);
}
}
}
}
contract Log {
struct LogEntry {
address actorId;
address userId;
string operation;
string[] processedData;
string serviceName;
}
mapping(uint256 => LogEntry) public logEntries;
uint256 public entryCount;
function addLogEntry(address _actorId, address _userId, string memory _operation, string[] memory _processedData, string memory _serviceName) public {
LogEntry memory newEntry = LogEntry(_actorId, _userId, _operation, _processedData, _serviceName);
logEntries[entryCount] = newEntry;
entryCount++;
}
function getLogEntry(uint256 index) public view returns (LogEntry memory) {
return logEntries[index];
}
}
contract Verification {
DataUsage dataUsage;
Agreement agreement;
Log log;
constructor(address _dataUsageAddress, address _agreementAddress, address _logAddress) {
dataUsage = DataUsage(_dataUsageAddress);
agreement = Agreement(_agreementAddress);
log = Log(_logAddress);
}
function checkLog() public view returns (address[] memory){
uint256 numActors = dataUsage.getActorAddressesCount();
uint256 numLogEntries = log.entryCount();
address[] memory violatingActors = new address[](numLogEntries);
uint256 violatingCount = 0;
//Iterating through log entries
for (uint256 i = 0; i < numLogEntries; i++) {
Log.LogEntry memory logEntry = log.getLogEntry(i);
bool consentFound = false;
//Iterating through actor count
for (uint16 j = 0; j < numActors; j++) {
address actorAddress = dataUsage.actorAddresses(j);
Agreement.Consent[] memory userConsents = agreement.getConsents(actorAddress, logEntry.userId);
//Iterating through user consent (actoraddress, index)
for (uint16 k = 0; k < userConsents.length; k++) {
Agreement.Consent memory consent = userConsents[k];
//verification contract - 1
if (logEntry.actorId == consent.actorID) {
SharedStructs.Service memory service = dataUsage.getService(actorAddress, k);
//verification contract - 2, 3
if (keccak256(abi.encodePacked(logEntry.operation)) == keccak256(abi.encodePacked(service.operation)) &&
keccak256(abi.encodePacked(logEntry.operation)) == keccak256(abi.encodePacked(consent.operation)) &&
isSubset(logEntry.processedData, consent.personalData)) {
//break from the inner loop once a matching consent is found.
consentFound = true;
break;
}
}
}
}
if (!consentFound) {
violatingActors[violatingCount] = logEntry.actorId;
violatingCount++;
}
}
//creating a new array that will store the address of the violating actors
address[] memory result;
//If no violations, print to console "no violations found" and return an empty array
if (violatingCount == 0) {
console.log("No violations found!");
return new address[](0);
} else {
console.log("Violations found!");
// Creating a temporary array to store unique addresses
address[] memory uniqueAddresses = new address[](violatingCount);
// Counting the number of unique addresses
uint uniqueCount = 0;
// Iterating through the violatingActors array and counting unique addresses
for (uint i = 0; i < violatingCount; i++) {
bool isNewAddress = true;
for (uint j = 0; j < uniqueCount; j++) {
if (uniqueAddresses[j] == violatingActors[i]) {
isNewAddress = false;
break;
}
}
if (isNewAddress) {
uniqueAddresses[uniqueCount] = violatingActors[i];
uniqueCount++;
}
}
// Creating a new array with the size of unique addresses
result = new address[](uniqueCount);
// Appending the unique addresses to the result array
for (uint i = 0; i < uniqueCount; i++) {
result[i] = uniqueAddresses[i];
}
console.log("The violating actors are:");
for (uint i = 0; i < result.length; i++) {
console.log(result[i]);
}
return result;
}
}
function isSubset(string[] memory subset, string[] memory superset) private pure returns (bool) {
for (uint256 i = 0; i < subset.length; i++) {
bool found = false;
for (uint256 j = 0; j < superset.length; j++) {
if (keccak256(abi.encodePacked(subset[i])) == keccak256(abi.encodePacked(superset[j]))) {
found = true;
break;
}
}
if (!found) {
return false;
}
}
return true;
}
}