Last active
October 22, 2022 07:30
-
-
Save satwikkansal/aeb88cb3cdafaf49b977cfa320c9c086 to your computer and use it in GitHub Desktop.
Ethereum Smart contract for an Exam Management system written in solidity
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity ^0.5.12; | |
contract Exam { | |
// The assessable element | |
// The Exam marking system which is responsible for invoking assessment functions. | |
address owner_ems; | |
address payable student; | |
// Main examiners | |
address primary_examiner_1; | |
address primary_examiner_2; | |
address primary_examiner_3; | |
// Additional examiners | |
address secondary_examiner_1; | |
address secondary_examiner_2; | |
address payable arbiter; | |
mapping (address => uint) examiner_marks; | |
mapping (address => bool) marks_submitted; | |
uint assigned_marks; | |
uint assigned_marks_timestamp; | |
constructor (address payable _student, | |
address _primary1, | |
address _primary2, | |
address _primary3, | |
address _secondary1, | |
address _secondary2, | |
address payable _arbiter, | |
address _ems) public { | |
student = _student; | |
primary_examiner_1 = _primary1; | |
primary_examiner_2 = _primary2; | |
primary_examiner_3 = _primary3; | |
secondary_examiner_1 = _secondary1; | |
secondary_examiner_2 = _secondary2; | |
arbiter = _arbiter; | |
} | |
function get_arbiter_address () public returns (address payable) { | |
return arbiter; | |
} | |
function get_student_address () public returns (address payable) { | |
return student; | |
} | |
function get_primary_examiner(uint examiner_number) public returns (address) { | |
if (examiner_number == 1) { | |
return primary_examiner_1; | |
} | |
if (examiner_number == 2) { | |
return primary_examiner_2; | |
} | |
if (examiner_number == 3) { | |
return primary_examiner_3; | |
} | |
} | |
function get_secondary_examiner(uint examiner_number) public returns (address) { | |
if (examiner_number == 1) { | |
return secondary_examiner_1; | |
} | |
if (examiner_number == 2) { | |
return secondary_examiner_2; | |
} | |
} | |
function assign_marks(uint marks) public { | |
require(msg.sender == owner_ems); | |
assigned_marks = marks; | |
assigned_marks_timestamp = block.timestamp; | |
} | |
function get_assigned_marks () public returns (uint) { | |
return assigned_marks; | |
} | |
function get_result_date () public returns (uint) { | |
return assigned_marks_timestamp; | |
} | |
function add_marks (address examiner, uint marks) public { | |
require(msg.sender == owner_ems); | |
examiner_marks[examiner] = marks; | |
marks_submitted[examiner] = true; | |
} | |
function is_primary_evaluation_submitted () public returns (bool) { | |
return (marks_submitted[primary_examiner_1] && | |
marks_submitted[primary_examiner_2] && | |
marks_submitted[primary_examiner_3]); | |
} | |
function get_primary_examiner_marks (uint examiner_number) public returns (uint) { | |
if (examiner_number == 1) { | |
return examiner_marks[primary_examiner_1]; | |
} | |
if (examiner_number == 2) { | |
return examiner_marks[primary_examiner_2]; | |
} | |
if (examiner_number == 3) { | |
return examiner_marks[primary_examiner_3]; | |
} | |
} | |
function is_secondary_evaluation_submitted () public returns (bool) { | |
return (marks_submitted[secondary_examiner_1] && | |
marks_submitted[secondary_examiner_2]); | |
} | |
function get_secondary_examiner_marks (uint examiner_number) public returns (uint) { | |
if (examiner_number == 1) { | |
return examiner_marks[secondary_examiner_1]; | |
} | |
if (examiner_number == 2) { | |
return examiner_marks[secondary_examiner_2]; | |
} | |
} | |
} | |
contract EMS { | |
address payable university; | |
enum EvaluationState {NoExamRegistered, PrimaryEvaluationDue, SecondaryEvaluationDue, SecondaryEvaluationDone, RevaluationInProgress, EvaluationFinished} | |
Exam public exam; | |
EvaluationState public current_state; | |
uint primary_difference_threshold = 10; | |
uint revaluation_difference_threshold = 5; | |
constructor () public { | |
current_state = EvaluationState.NoExamRegistered; | |
university = msg.sender; | |
} | |
function register_exam (Exam _exam) public { | |
require(current_state == EvaluationState.NoExamRegistered); | |
exam = _exam; | |
current_state = EvaluationState.PrimaryEvaluationDue; | |
} | |
function submit_primary_evaluation (uint marks) public { | |
require(current_state == EvaluationState.PrimaryEvaluationDue); | |
require(msg.sender == exam.get_primary_examiner(1) || msg.sender == exam.get_primary_examiner(2) || msg.sender == exam.get_primary_examiner(3)); | |
exam.add_marks(msg.sender, marks); | |
} | |
function submit_secondary_evaluation (uint marks) public { | |
require(current_state == EvaluationState.SecondaryEvaluationDue); | |
require(msg.sender == exam.get_secondary_examiner(1) || msg.sender == exam.get_secondary_examiner(2)); | |
exam.add_marks(msg.sender, marks); | |
} | |
function finalize_primary_evaluation () public { | |
require(current_state == EvaluationState.PrimaryEvaluationDue); | |
require(exam.is_primary_evaluation_submitted()); | |
uint marks1 = exam.get_primary_examiner_marks(1); | |
uint marks2 = exam.get_primary_examiner_marks(2); | |
uint marks3 = exam.get_primary_examiner_marks(3); | |
(uint highest, uint lowest) = get_highest_and_lowest_of_three(marks1, marks2, marks3); | |
uint difference = highest - lowest; | |
if (difference > primary_difference_threshold) { | |
// If assigned marks are NOT within 10 points of each other, mark it open for evaluation by secondary examiners. | |
current_state = EvaluationState.SecondaryEvaluationDue; | |
} else { | |
// If assigned marks are within 10 points of each other, then the result is calculated to be the average of the three. | |
uint average = (marks1 + marks2 + marks3) / 3; | |
exam.assign_marks(average); | |
current_state = EvaluationState.EvaluationFinished; | |
} | |
} | |
function finalize_secondary_evaluation () public { | |
require(current_state == EvaluationState.SecondaryEvaluationDue); | |
require(exam.is_secondary_evaluation_submitted()); | |
uint pmarks1 = exam.get_primary_examiner_marks(1); | |
uint pmarks2 = exam.get_primary_examiner_marks(2); | |
uint pmarks3 = exam.get_primary_examiner_marks(3); | |
uint smarks1 = exam.get_secondary_examiner_marks(1); | |
uint smarks2 = exam.get_secondary_examiner_marks(2); | |
(uint phighest, uint plowest) = get_highest_and_lowest_of_three(pmarks1, pmarks2, pmarks3); | |
(uint highest, uint temp1) = get_highest_and_lowest_of_three(phighest, smarks2, smarks1); | |
(uint temp2, uint lowest) = get_highest_and_lowest_of_three(plowest, smarks2, smarks1); | |
// The highest and lowest mark are discarded and the average of the remaining three is adopted as the actual mark. | |
uint sum_of_five = pmarks1 + pmarks2 + pmarks3 + smarks1 + smarks2; | |
uint average_of_three = (sum_of_five - highest - lowest) / 3; | |
exam.assign_marks(average_of_three); | |
current_state = EvaluationState.SecondaryEvaluationDone; | |
} | |
function accept_marks () public { | |
require(msg.sender == exam.get_student_address()); | |
require(current_state == EvaluationState.SecondaryEvaluationDone); | |
// Mark evaluation as finished. | |
current_state = EvaluationState.EvaluationFinished; | |
} | |
function initiate_revaluation () public payable { | |
// If the decision required additional examiners, the student involved may pay 100 Finney* and request a revision of paper | |
// within 30 days of publication of the result. | |
require(current_state == EvaluationState.SecondaryEvaluationDone); | |
require(msg.value == 100 finney); | |
require(msg.sender == exam.get_student_address()); | |
require(exam.get_result_date() + 30 days > block.timestamp); | |
current_state = EvaluationState.RevaluationInProgress; | |
} | |
function submit_revaluation (uint new_marks) public { | |
require(current_state == EvaluationState.RevaluationInProgress); | |
address payable arbiter = exam.get_arbiter_address(); | |
require(msg.sender == arbiter); | |
uint current_marks = exam.get_assigned_marks(); | |
uint difference = current_marks - new_marks; | |
address payable student = exam.get_student_address(); | |
if (difference > revaluation_difference_threshold || difference < -revaluation_difference_threshold) { | |
// If the updated mark is NOT within 5 marks of the originally assigned one, | |
// the university gets 20 Finney and the student is refunded the remaining 60 Finney | |
university.transfer(20 finney); | |
student.transfer(60 finney); | |
} else { | |
// If the updated mark is within 5 marks of the originally assigned one, the university keeps the remaining 80 Finney | |
university.transfer(80 finney); | |
} | |
// The mark is updated as per the final examiner’s opinion. | |
exam.assign_marks(new_marks); | |
// In case of a revision of paper, the arbiter gets to assign an updated mark. He or she gets paid 20 Finney (of what the student paid). | |
arbiter.transfer(20 finney); | |
// Mark evaluation as finished. | |
current_state = EvaluationState.EvaluationFinished; | |
} | |
function get_highest_and_lowest_of_three (uint marks1, uint marks2, uint marks3) private returns (uint, uint) { | |
uint highest; | |
uint lowest; | |
if (marks1 > marks2) { | |
// marks1 can be the highest or second highest | |
if (marks1 > marks3) highest = marks1; | |
else highest = marks3; | |
// marks2 can be the lowest or second lowest | |
if (marks2 < marks3) lowest = marks2; | |
else lowest = marks3; | |
} else { | |
// marks2 can be the highest or second highest | |
if (marks2 > marks3) highest = marks2; | |
else highest = marks3; | |
// marks1 can be the lowest or second lowest | |
if (marks1 < marks3) lowest = marks1; | |
else lowest = marks3; | |
} | |
return (highest, lowest); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
A = Action | |
R = Reqiured Constraints | |
A: Initialize exam marking system contract. | |
A: Initialize Exam contract. | |
A: Register the Exam contract with Exam Marking System contract. | |
A: Submit primary evaluation mark. | |
R: Role-match i.e. The invoking address should be a primary evaluator. | |
A: Finalize primary evaluation. | |
R: All 3 examiners should have submitted their marks. | |
A: Submit secondary evaluation. | |
R: Delta b/w primary evaluation should be > 10, and Role-match. | |
A: finalize_secondary_evaluation | |
R: All 2 secondary examiners should have submitted their marks. | |
A: accept_marks | |
R: Role-match with student | |
A: initiate_revaluation | |
R: Role-match with student | |
A: submit_revaluation | |
R: Role-match with Arbiter, Student should have initiated revaluation already. | |
End | |
{NoExamRegistered, PrimaryEvaluationDue, SecondaryEvaluationDue, SecondaryEvaluationDone, RevaluationInProgress, EvaluationFinished} | |
*/ | |
pragma solidity ^0.5.12; | |
contract Exam { | |
constructor (address payable _student, | |
address _primary1, | |
address _primary2, | |
address _primary3, | |
address _secondary1, | |
address _secondary2, | |
address payable _arbiter) public { | |
} | |
function get_arbiter_address () public returns (address payable) { | |
} | |
function get_student_address () public returns (address payable) { | |
} | |
function get_primary_examiner(uint examiner_number) public returns (address) { | |
} | |
function get_secondary_examiner(uint examiner_number) public returns (address) { | |
} | |
function assign_marks(uint marks) public { | |
} | |
function get_assigned_marks () public returns (uint) { | |
} | |
function get_result_date () public returns (uint) { | |
} | |
function add_marks (address examiner, uint marks) public { | |
} | |
function is_primary_evaluation_submitted () public returns (bool) { | |
} | |
function get_primary_examiner_marks (uint examiner_number) public returns (uint) { | |
} | |
function is_secondary_evaluation_submitted () public returns (bool) { | |
} | |
function get_secondary_examiner_marks (uint examiner_number) public returns (uint) { | |
} | |
} | |
contract EMS { | |
constructor () public { | |
} | |
function register_exam (Exam _exam) public { | |
} | |
function submit_primary_evaluation (uint marks) public { | |
} | |
function submit_secondary_evaluation (uint marks) public { | |
} | |
function finalize_primary_evaluation () public { | |
// If assigned marks are NOT within 10 points of each other, mark it open for evaluation by secondary examiners | |
// If assigned marks are within 10 points of each other, then the result is calculated to be the average of the three. | |
} | |
function finalize_secondary_evaluation () public { | |
// The highest and lowest mark are discarded and the average of the remaining three is adopted as the actual mark. | |
} | |
function accept_marks () public { | |
} | |
function initiate_revaluation () public payable { | |
// If the decision required additional examiners, the student involved may pay 100 Finney* and request a revision of paper | |
// within 30 days of publication of the result. | |
} | |
function submit_revaluation (uint new_marks) public { | |
// If the updated mark is NOT within 5 marks of the originally assigned one, | |
// the university gets 20 Finney and the student is refunded the remaining 60 Finney | |
// If the updated mark is within 5 marks of the originally assigned one, the university keeps the remaining 80 Finney | |
// The mark is updated as per the final examiner’s opinion. | |
// In case of a revision of paper, the arbiter gets to assign an updated mark. He or she gets paid 20 Finney (of what the student paid). | |
// Mark evaluation as finished. | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment