Skip to content

Commit

Permalink
Ag 19 DECsRegistry smart contract (#8)
Browse files Browse the repository at this point in the history
* fix(@script): AG-19 removed comments

removed code comments

* docs(@docs): AG-19 updated sequence diagrams

updated the sequence diagrams images for the functional analysis

* docs(@docs): AG-19 updated smart contract classes

updated docs about the smart contracts classes

* feat(@contracts): AG-19 DEC contract implementation

implemented a first version of the DEC contract

* feat(@contracts): AG-19 added events to DEC sc

emitted event in the DEC smart contract implementation

* feat(@contracts): AG-19 implemented DEC contract

implemented DEC contract and related unit tests

* fix(@contracts): AG-19 removed event

removed event not used in DEC contract

* feat(@contracts): AG-19 DECs registry implementation

implemented the register of the DECs with the required methods

* test(@contracts): AG-19 defined test for DECsRegistry contract

defined test structure for the DECsRegistry smart contract

* test(@contracts): AG-19 implemented unit test

implemented unit tests for DECsRegistry smart contract

* feat(@contracts): AG-19 added events to smart contract

added events to the DECsRegistry smart contract
  • Loading branch information
g3k0 authored Mar 29, 2024
1 parent 56f40f9 commit db20673
Show file tree
Hide file tree
Showing 17 changed files with 249 additions and 168 deletions.
62 changes: 62 additions & 0 deletions contracts/DEC.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.24;

/// @title The Voter's Digital Electoral Cards
/// @author Christian Palazzo <palazzochristian@yahoo.it>
/// @custom:experimental This is an experimental contract.
contract DEC {

address public owner;

constructor() {
/// @dev only the owner of the contract has write permissions
owner = msg.sender;
}

modifier onlyOwner() {
require(msg.sender == owner, "Only owner can call this function");
_;
}


/// @notice This is the Digital Electoral Card, emitted by a public third-party authority and owned by the Voter
/// @dev This data is encrypted with the Voter's public address and only the Voter can decrypt it using the private key
struct decData {
string taxCode;
string municipality;
string province;
string region;
string country;
}

event DECEncrypted(address indexed owner, bytes encryptedData);


/// @notice This function is used to encrypt ad digitally sign a DEC
function encryptDEC(decData memory dec) public onlyOwner returns (bytes memory) {
bytes memory encodedData = abi.encodePacked(
dec.taxCode, dec.municipality, dec.province, dec.region, dec.country
);
bytes32 hashedData = keccak256(encodedData);
bytes memory signature = signData(hashedData);

emit DECEncrypted(msg.sender, abi.encodePacked(hashedData, signature));

return abi.encodePacked(hashedData, signature);
}


/// @notice This function is used to digitally sign the data
function signData(bytes32 data) private pure returns (bytes memory) {
bytes32 hash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", data));
bytes1 v = bytes1(0);
bytes32 r = bytes32(0);
bytes32 s = uintToBytes32(1);
return abi.encodePacked(ecrecover(hash, uint8(v), r, s), r, s);
}

/// @notice this function is used in signData function
function uintToBytes32(uint256 x) private pure returns (bytes32) {
return bytes32(x);
}
}
61 changes: 61 additions & 0 deletions contracts/DECsRegistry.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.24;

import "./DEC.sol";

/// @title The Registry of the Digital Electoral Cards
/// @author Christian Palazzo <palazzochristian@yahoo.it>
/// @custom:experimental This is an experimental contract.
contract DECsRegistry is DEC {

constructor() DEC() {

}

/// @notice this is the list of stamps of elections in which the voter participated
/// @dev the first address is related to the Voter's EOA, the second array is the Voter's stamps list
mapping (address => address[]) electoralStamps;

/// @notice this function contains the list of DECs
/// @dev the address is related to the Voter's EOA
mapping (address => bytes) registry;

event DECRegistered(address indexed voter, bytes dec);
event DECStamped(address indexed election, address indexed voter);


/// @notice this function is used by the third party authority to register a Voter's DEC in the registry
/// @dev the DEC contains sensitive data that must be encrypted
function registerDEC(decData memory dec, address voter) public onlyOwner {
require(registry[voter].length == 0, "The Voter's DEC has been already registered");
registry[voter] = encryptDEC(dec);
emit DECRegistered(voter, registry[voter]);
return;
}


/// @notice this function returns an encrypted DEC in order to check if a Voter has the voting rights
function getDEC(address voter) public view returns(bytes memory) {
require(registry[voter].length != 0, "The Voter don't have a registered DEC");
return registry[voter];
}


/// @notice this function checks in the registry if the Voter already voted in a certail election
function hasVoterAlreadyVoted(address voter, address election) public view returns (bool) {
for (uint i = 0; i < electoralStamps[voter].length; i++) {
if (electoralStamps[voter][i] == election) {
return true;
}
}
return false;
}

/// @notice this function put the election stamp on the Voter's DEC after the vote
/// @dev the owner of the DECs registry is the same of the election smart contract (third party authority)
function stampsTheDEC(address election, address voter) public onlyOwner {
electoralStamps[voter].push(election);
emit DECStamped(election, voter);
return;
}
}
34 changes: 0 additions & 34 deletions contracts/Lock.sol

This file was deleted.

Binary file modified docs/assets/agora_main_sequence_diagrams-Phase 0.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/assets/agora_main_sequence_diagrams-Phase 2.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/assets/agora_main_sequence_diagrams-Phase 3.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/assets/agora_main_sequence_diagrams-Phase 4.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/decs_registry_class.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/election_class.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/assets/electoral_card_class.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed docs/assets/registration_class.png
Binary file not shown.
Binary file removed docs/assets/voting_class.png
Binary file not shown.
7 changes: 0 additions & 7 deletions script/create-voter-eoa.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,6 @@ import { ethers } from "hardhat";
import { main } from "./create-voter-eoa";

describe("Main function", () => {
/*beforeEach(() => {
ethers.Wallet.createRandom = jest.fn().mockReturnValueOnce({
address: "mockedAddress",
privateKey: "mockedPrivateKey",
});
});*/

afterEach(() => {
jest.clearAllMocks();
});
Expand Down
35 changes: 35 additions & 0 deletions test/DEC.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { ethers } from "hardhat";
import { expect } from "chai";

describe("DEC Contract", function () {
let DEC: any;
let decContract: any;
let ownerAddress: any;

before(async function () {
DEC = await ethers.getContractFactory("DEC");
[ownerAddress] = await ethers.getSigners();
});

beforeEach(async function () {
decContract = await DEC.deploy();
});

it("should deploy the contract and set the owner", async function () {
expect(await decContract.owner()).to.equal(ownerAddress.address);
});

it("should encrypt DEC data correctly", async function () {
const decData = {
taxCode: "123456789",
municipality: "Sample Municipality",
province: "Sample Province",
region: "Sample Region",
country: "Sample Country",
};

const encryptedData = await decContract.encryptDEC(decData);

expect(encryptedData.data).to.not.be.null;
});
});
84 changes: 84 additions & 0 deletions test/DECsRegistry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { ethers } from "hardhat";
import { expect } from "chai";
import { Signer } from "ethers";
import { DECsRegistry } from "../typechain-types/DECsRegistry";
import { DecData } from "./types";

describe("DECs Registry Contract", function () {
let contract: DECsRegistry;
let owner: Signer;
let voter: Signer;

const electionAddress = "0xae92d5aD7583AD66E49A0c67BAd18F6ba52dDDc1";
const decData: DecData = {
taxCode: "1234567890",
municipality: "mockMunicipality",
province: "mockProvince",
region: "mockRegion",
country: "mockCountry",
};

beforeEach(async () => {
const ContractFactory = await ethers.getContractFactory("DECsRegistry");
[owner, voter] = await ethers.getSigners();
contract = await ContractFactory.deploy();
});

it("Should deploy the contract", async function () {
expect(contract.address).to.not.equal(0);
});

it("Should register DEC", async function () {
const response = await contract
.connect(owner)
.registerDEC(decData, await voter.getAddress());

expect(response.blockHash).to.not.equal(null);
expect(response.blockHash).to.not.equal(undefined);
expect(response.data.length).to.be.greaterThan(0);
});

it("Should not register DEC if already registered", async function () {
await contract
.connect(owner)
.registerDEC(decData, await voter.getAddress());
await expect(
contract.connect(owner).registerDEC(decData, await voter.getAddress()),
).to.be.revertedWith("The Voter's DEC has been already registered");
});

it("Should get DEC", async function () {
await contract
.connect(owner)
.registerDEC(decData, await voter.getAddress());
const retrievedDEC = await contract.getDEC(await voter.getAddress());

expect(retrievedDEC.length).to.be.greaterThan(0);
});

it("Should revert if DEC not registered", async function () {
await expect(contract.getDEC(await voter.getAddress())).to.be.revertedWith(
"The Voter don't have a registered DEC",
);
});

it("Should return true if voter already voted", async function () {
await contract
.connect(owner)
.stampsTheDEC(electionAddress, await voter.getAddress());

const hasVoted = await contract.hasVoterAlreadyVoted(
await voter.getAddress(),
electionAddress,
);
expect(hasVoted).to.be.true;
});

it("Should return false if voter hasn't voted", async function () {
const hasVoted = await contract.hasVoterAlreadyVoted(
await voter.getAddress(),
electionAddress,
);
expect(hasVoted).to.be.false;
});
});
Loading

0 comments on commit db20673

Please sign in to comment.