diff --git a/admin.html b/admin.html new file mode 100644 index 0000000..c7da378 --- /dev/null +++ b/admin.html @@ -0,0 +1,19 @@ + + + + Administrator + + + + + + + Student Listing + Issuer Listing + Back + + + + \ No newline at end of file diff --git a/bs-config.json b/bs-config.json new file mode 100644 index 0000000..b71247e --- /dev/null +++ b/bs-config.json @@ -0,0 +1,4 @@ +{ + "port": 80, + "open": false +} \ No newline at end of file diff --git a/certificate.html b/certificate.html new file mode 100644 index 0000000..b5c5b71 --- /dev/null +++ b/certificate.html @@ -0,0 +1,38 @@ + + + + Create Certificate + + + + + + +
+ +
+ +
+
+ +
+
+ +
+
+ +
+ + + Back +
+ + + + + \ No newline at end of file diff --git a/contracts/Master.sol b/contracts/Master.sol new file mode 100644 index 0000000..7fa9f6f --- /dev/null +++ b/contracts/Master.sol @@ -0,0 +1,238 @@ +pragma solidity ^0.4.23; + +contract Master { + + struct Certificate { + address student; + address issuer; + string courseTitle; + string completionDate; + string expiryDate; + } + + struct Student { + string name; + } + + struct Issuer { + string issuerName; + string logoUrl; + string issuerAddress; + } + + mapping (address => Student) private students; + address[] private studentAddresses; + + mapping (address => Issuer) private issuers; + address[] private issuerAddresses; + + Certificate[] private certificates; + + address private adminOwner; + + constructor() public { + adminOwner = msg.sender; + } + + // create new student (admin) + function createStudent(address _address, string _name) public { + + // Check current user is admin + require(adminOwner == msg.sender); + + // Check if account already exists + validateAddress(_address); + + // Add a new student contract to students map + students[_address] = Student({ + name: _name + }); + + // Add the address to studentAddresses array + studentAddresses.push(_address); + } + + function listStudentAddresses() public view returns (address[]) { + + return studentAddresses; + } + + function listIssuerAddresses() public view returns (address[]) { + + return issuerAddresses; + } + + function getStudentName(address _address) public view returns (string) { + + for (uint count = 0; count < studentAddresses.length; count++) { + if (studentAddresses[count] == _address) { + return students[_address].name; + } + } + + revert(); + } + + function getIssuerName(address _address) public view returns (string) { + + for (uint count = 0; count < issuerAddresses.length; count++) { + if (issuerAddresses[count] == _address) { + return issuers[_address].issuerName; + } + } + + revert(); + } + + function getIssuerDetails(address _address) public view returns (string, string, string) { + + for (uint count = 0; count < issuerAddresses.length; count++) { + if (issuerAddresses[count] == _address) { + Issuer storage issuer = issuers[_address]; + return (issuer.issuerName, issuer.logoUrl, issuer.issuerAddress); + } + } + + revert(); + } + + // create new issuer (admin) + function createIssuer(address _address, string _name, string _issuerAddress, string _logoUrl) public { + + // Check current user is admin + require(adminOwner == msg.sender); + + // Check if account already exists + validateAddress(_address); + + // Add a new issuer contract to issuers map + issuers[_address] = Issuer({ + issuerName: _name, + issuerAddress: _issuerAddress, + logoUrl: _logoUrl + }); + + // Add the address to issuerAddresses array + issuerAddresses.push(_address); + } + + function validateAddress(address _address) view private { + + // Check if account already exists + for (uint count = 0; count < studentAddresses.length; count++) { + if (studentAddresses[count] == _address) { + revert(); + } + } + + for (count = 0; count < issuerAddresses.length; count++) { + if (issuerAddresses[count] == _address) { + revert(); + } + } + + if (_address == adminOwner) { + revert(); + } + } + + // create new certificate (issuer) + function createCertificate(address student, string courseTitle, string completionDate, string expiryDate) public { + + for (uint studentCount = 0; studentCount < studentAddresses.length; studentCount++) { + if (studentAddresses[studentCount] == student) { + for (uint issuerCount = 0; issuerCount < issuerAddresses.length; issuerCount++) { + if (issuerAddresses[issuerCount] == msg.sender) { + certificates.push(Certificate({ + student: student, + issuer: msg.sender, + courseTitle: courseTitle, + completionDate: completionDate, + expiryDate: expiryDate + })); + return; + } + } + } + } + + revert(); + } + + // Verify a certificate + function verifyCertificate(address student, address issuer, string courseTitle) public view returns (uint) { + + for (uint count = 0; count < certificates.length; count++) { + Certificate storage certificate = certificates[count]; + if (certificate.student == student && certificate.issuer == issuer && equal(certificate.courseTitle, courseTitle)) { + return count + 1; + } + } + + // validate expiry date + + return 0; + } + + // Student to query his/her own certificates + function getStudentCertificates() public view returns (uint[]) { + + uint[] memory studentCertificates = new uint[](certificates.length); + uint index = 0; + for (uint count = 0; count < certificates.length; count++) { + if (certificates[count].student == msg.sender) { + studentCertificates[index++] = count; + } + } + return studentCertificates; + } + + // Issuer to query issued certificates + function getIssuerCertificates() public view returns (uint[]) { + + uint[] memory issuedCertificates = new uint[](certificates.length); + uint index = 0; + for (uint count = 0; count < certificates.length; count++) { + if (certificates[count].issuer == msg.sender) { + issuedCertificates[index++] = count; + } + } + return issuedCertificates; + } + + function getCertificateDetails(uint index) public view returns (string, string, string, string, string) { + + require(index < certificates.length); + Certificate storage certificate = certificates[index]; + return (issuers[certificate.issuer].issuerName, students[certificate.student].name, certificate.courseTitle, certificate.completionDate, certificate.expiryDate); + } + + function compare(string _a, string _b) private pure returns (int) { + + bytes memory a = bytes(_a); + bytes memory b = bytes(_b); + uint minLength = a.length; + if (b.length < minLength) minLength = b.length; + for (uint i = 0; i < minLength; i ++) + if (a[i] < b[i]) + return -1; + else if (a[i] > b[i]) + return 1; + if (a.length < b.length) + return -1; + else if (a.length > b.length) + return 1; + else + return 0; + } + + function equal(string _a, string _b) private pure returns (bool) { + + return compare(_a, _b) == 0; + } + + // edit new student (admin / student) + // edit new issuer (admin / issuer) + // revoke new certificate (issuer) + +} \ No newline at end of file diff --git a/deploy-contract.js b/deploy-contract.js new file mode 100644 index 0000000..c47dfb9 --- /dev/null +++ b/deploy-contract.js @@ -0,0 +1,33 @@ +console.log('Deploy started on ' + new Date()); +console.log('Loading libraries'); + +var fs = require('fs'); +var Web3 = require('web3'); +var solc = require('solc'); +var web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); + +console.log('Compiling Solidity contracts'); + +var compiled = solc.compile(fs.readFileSync('contracts/Master.sol').toString()); +var abiDefinition = compiled.contracts[':Master'].interface; +console.log('ABI Definition: ' + abiDefinition); + +var byteCode = compiled.contracts[':Master'].bytecode; +var estimatedGas = web3.eth.estimateGas({data: byteCode}); +console.log('Deploying contracts using estimated gas ' + estimatedGas); + +var contract = web3.eth.contract(JSON.parse(abiDefinition)).new(null, {data: byteCode, from: web3.eth.accounts[0], gas: estimatedGas}); + +new Promise((resolve) => setTimeout(resolve, 1000)).then(() => { + + console.log('Contract address: ' + contract.address); + console.log('Writing dummy data'); + console.log('Creating issuer'); + contract.createIssuer(web3.eth.accounts[1], 'HKU', 'Pokfulam Road, HK', 'http://avuou.hku.hk/guestpage/HKUbw2.gif', {from: web3.eth.accounts[0], gas: contract.createIssuer.estimateGas(web3.eth.accounts[1], 'HKU', 'Pokfulam Road, HK', 'http://avuou.hku.hk/guestpage/HKUbw2.gif')}); + console.log('Creating student'); + contract.createStudent(web3.eth.accounts[2], 'Simon Chan', {from: web3.eth.accounts[0], gas: contract.createStudent.estimateGas(web3.eth.accounts[2], 'Simon Chan')}); + console.log('Creating certificate'); + web3.eth.defaultAccount = web3.eth.accounts[1]; + contract.createCertificate(web3.eth.accounts[2], 'Introduction to Blockchain', '2018-07-01', '2028-08-31', {from: web3.eth.accounts[1], gas: contract.createCertificate.estimateGas(web3.eth.accounts[2], 'Introduction to Blockchain', '2018-07-01', '2028-08-31')}); + console.log('Deploy complete at ' + new Date()); +}); \ No newline at end of file diff --git a/images/cert-template.gif b/images/cert-template.gif new file mode 100644 index 0000000..daf2eb3 Binary files /dev/null and b/images/cert-template.gif differ diff --git a/images/certificate.ico b/images/certificate.ico new file mode 100644 index 0000000..fee78c3 Binary files /dev/null and b/images/certificate.ico differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..168a76c --- /dev/null +++ b/index.html @@ -0,0 +1,20 @@ + + + + Certificate Blockchain Apps + + + + + + + Verify Certificate + I am a student + I am an issuer + I am the administrator + + + + \ No newline at end of file diff --git a/issuer-certificates.html b/issuer-certificates.html new file mode 100644 index 0000000..084efb7 --- /dev/null +++ b/issuer-certificates.html @@ -0,0 +1,57 @@ + + + + Issued Certificates + + + + + + + + + + +
+ + + + + + + + + + + + +
+ Create Certificate + Back + + + \ No newline at end of file diff --git a/issuer-list.html b/issuer-list.html new file mode 100644 index 0000000..bccc78b --- /dev/null +++ b/issuer-list.html @@ -0,0 +1,33 @@ + + + + Issuer Listing + + + + + + +
+ + + + + + + + + + + + +
+ Create Issuer + Back + + + + + \ No newline at end of file diff --git a/issuer.html b/issuer.html new file mode 100644 index 0000000..1f1f6a3 --- /dev/null +++ b/issuer.html @@ -0,0 +1,38 @@ + + + + Create Issuer + + + + + + +
+ +
+ +
+
+ +
+
+ +
+
+ +
+ + + Cancel +
+ + + + + \ No newline at end of file diff --git a/js/certificate.js b/js/certificate.js new file mode 100644 index 0000000..3341282 --- /dev/null +++ b/js/certificate.js @@ -0,0 +1,52 @@ +$(document).ready(function() { + + var web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); + + if (web3.isConnected()) { + + console.log('Web3 successfully connected'); + var issuerAccount = web3.eth.accounts[1]; + var abiDefinition = [{"constant":true,"inputs":[{"name":"_address","type":"address"}],"name":"getIssuerName","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_address","type":"address"}],"name":"getStudentName","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getIssuerCertificates","outputs":[{"name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"listStudentAddresses","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_address","type":"address"}],"name":"getIssuerDetails","outputs":[{"name":"","type":"string"},{"name":"","type":"string"},{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"student","type":"address"},{"name":"courseTitle","type":"string"},{"name":"completionDate","type":"string"},{"name":"expiryDate","type":"string"}],"name":"createCertificate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"index","type":"uint256"}],"name":"getCertificateDetails","outputs":[{"name":"","type":"string"},{"name":"","type":"string"},{"name":"","type":"string"},{"name":"","type":"string"},{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_address","type":"address"},{"name":"_name","type":"string"},{"name":"_issuerAddress","type":"string"},{"name":"_logoUrl","type":"string"}],"name":"createIssuer","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_address","type":"address"},{"name":"_name","type":"string"}],"name":"createStudent","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"listIssuerAddresses","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getStudentCertificates","outputs":[{"name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"student","type":"address"},{"name":"issuer","type":"address"},{"name":"courseTitle","type":"string"}],"name":"verifyCertificate","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]; + var contractAddress = '0x64ec4b719fa6b1fb835b7212e1973236be746989'; + var masterContract = web3.eth.contract(abiDefinition); + var contractInstance = masterContract.at(contractAddress); + web3.eth.defaultAccount = issuerAccount; + + try { + var issuerName = contractInstance.getIssuerName(issuerAccount); + $('h1').html('Create Certificate - ' + issuerName); + } catch (e) { + console.log(e); + $('.alert-danger').html('Issuer not found.').show(); + } + + $('form').submit(function (event) { + $('.alert-danger, .alert-success').hide(); + var courseTitle = $("#course-title").val(); + var completionDate = $("#completion-date").val(); + var expiryDate = $("#expiry-date").val(); + var address = $("#address").val(); + var index = web3.eth.accounts.indexOf(address); + + if (index < 0) { + $('.alert-danger').html('Account address not found.').show(); + console.log('Valid addresses', web3.eth.accounts); + } else { + try { + var estimatedGas = contractInstance.createCertificate.estimateGas(address, courseTitle, completionDate, expiryDate); + contractInstance.createCertificate(address, courseTitle, completionDate, expiryDate, {from: issuerAccount, gas: estimatedGas}); + $('.alert-success').show(); + $('#course-title, #completion-date, #expiry-date, #address').val(''); + } catch (e) { + console.log(e); + $('.alert-danger').html('Error occurred.').show(); + } + } + + event.preventDefault(); + }); + } else { + $('form').prepend('
Blockchain not connected
'); + $(':submit').prop('disabled', true); + } +}); \ No newline at end of file diff --git a/js/issuer-certificates.js b/js/issuer-certificates.js new file mode 100644 index 0000000..f9a88be --- /dev/null +++ b/js/issuer-certificates.js @@ -0,0 +1,50 @@ +$(document).ready(function() { + + var web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); + + if (web3.isConnected()) { + + console.log('Web3 successfully connected'); + var issuerAccount = web3.eth.accounts[1]; + var abiDefinition = [{"constant":true,"inputs":[{"name":"_address","type":"address"}],"name":"getIssuerName","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_address","type":"address"}],"name":"getStudentName","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getIssuerCertificates","outputs":[{"name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"listStudentAddresses","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_address","type":"address"}],"name":"getIssuerDetails","outputs":[{"name":"","type":"string"},{"name":"","type":"string"},{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"student","type":"address"},{"name":"courseTitle","type":"string"},{"name":"completionDate","type":"string"},{"name":"expiryDate","type":"string"}],"name":"createCertificate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"index","type":"uint256"}],"name":"getCertificateDetails","outputs":[{"name":"","type":"string"},{"name":"","type":"string"},{"name":"","type":"string"},{"name":"","type":"string"},{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_address","type":"address"},{"name":"_name","type":"string"},{"name":"_issuerAddress","type":"string"},{"name":"_logoUrl","type":"string"}],"name":"createIssuer","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_address","type":"address"},{"name":"_name","type":"string"}],"name":"createStudent","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"listIssuerAddresses","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getStudentCertificates","outputs":[{"name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"student","type":"address"},{"name":"issuer","type":"address"},{"name":"courseTitle","type":"string"}],"name":"verifyCertificate","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]; + var contractAddress = '0x64ec4b719fa6b1fb835b7212e1973236be746989'; + var masterContract = web3.eth.contract(abiDefinition); + var contractInstance = masterContract.at(contractAddress); + web3.eth.defaultAccount = issuerAccount; + + try { + var issuerName = contractInstance.getIssuerName(issuerAccount); + $('h1').html('Issued Certificates - ' + issuerName); + $('#issuer-name').text(issuerName); + } catch (e) { + console.log(e); + $('table').replaceWith('
Issuer not found
'); + } + + var certificates = contractInstance.getIssuerCertificates(); + console.log(certificates.length + " certificate record(s) loaded"); + + if (certificates.length > 0) { + certificates.forEach(function (index) { + var certificateDetails = contractInstance.getCertificateDetails(index); + var expiry = certificateDetails[4] ? certificateDetails[4] : "N/A"; + var student = '' + certificateDetails[1] + ''; + $('table tbody').append('' + student + '' + certificateDetails[2] + '' + certificateDetails[3] + '' + expiry + ''); + $('table').show(); + }); + } else { + $('table').replaceWith('
No issued certificates
'); + } + $('table').on('click', '.cert-button', function () { + var student = $(this).text(); + var courseTitle = $(this).parent().next(); + var completionDate = courseTitle.next(); + $('#student-name').text(student); + $('#course-title').text(courseTitle.text()); + $('#completion-date').text(completionDate.text()); + }); + } else { + $('table').replaceWith('
Blockchain not connected
'); + } + +}); \ No newline at end of file diff --git a/js/issuer-list.js b/js/issuer-list.js new file mode 100644 index 0000000..2483f92 --- /dev/null +++ b/js/issuer-list.js @@ -0,0 +1,29 @@ +$(document).ready(function() { + + var web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); + + if (web3.isConnected()) { + + console.log('Web3 successfully connected'); + var abiDefinition = [{"constant":true,"inputs":[{"name":"_address","type":"address"}],"name":"getIssuerName","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_address","type":"address"}],"name":"getStudentName","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getIssuerCertificates","outputs":[{"name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"listStudentAddresses","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_address","type":"address"}],"name":"getIssuerDetails","outputs":[{"name":"","type":"string"},{"name":"","type":"string"},{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"student","type":"address"},{"name":"courseTitle","type":"string"},{"name":"completionDate","type":"string"},{"name":"expiryDate","type":"string"}],"name":"createCertificate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"index","type":"uint256"}],"name":"getCertificateDetails","outputs":[{"name":"","type":"string"},{"name":"","type":"string"},{"name":"","type":"string"},{"name":"","type":"string"},{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_address","type":"address"},{"name":"_name","type":"string"},{"name":"_issuerAddress","type":"string"},{"name":"_logoUrl","type":"string"}],"name":"createIssuer","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_address","type":"address"},{"name":"_name","type":"string"}],"name":"createStudent","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"listIssuerAddresses","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getStudentCertificates","outputs":[{"name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"student","type":"address"},{"name":"issuer","type":"address"},{"name":"courseTitle","type":"string"}],"name":"verifyCertificate","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]; + var contractAddress = '0x64ec4b719fa6b1fb835b7212e1973236be746989'; + var masterContract = web3.eth.contract(abiDefinition); + var contractInstance = masterContract.at(contractAddress); + var issuerAddresses = contractInstance.listIssuerAddresses(); + console.log(issuerAddresses.length + ' issuer record(s) loaded'); + + if (issuerAddresses.length > 0) { + issuerAddresses.forEach(function (address) { + var issuerDetails = contractInstance.getIssuerDetails(address); + var businessAddress = issuerDetails[2] ? issuerDetails[2] : '-'; + var imageHtml = issuerDetails[1] ? '' : '-'; + $('table tbody').append('' + issuerDetails[0] + '' + address + '' + businessAddress + '' + imageHtml + ''); + $('table').show(); + }); + } else { + $('table').replaceWith('
No issuer accounts in the system
'); + } + } else { + $('table').replaceWith('
Blockchain not connected
'); + } +}); \ No newline at end of file diff --git a/js/issuer.js b/js/issuer.js new file mode 100644 index 0000000..3ae9e3c --- /dev/null +++ b/js/issuer.js @@ -0,0 +1,45 @@ +$(document).ready(function() { + + var web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); + + if (web3.isConnected()) { + + console.log('Web3 successfully connected'); + var account = web3.eth.accounts[0]; + web3.personal.unlockAccount(account); + + var abiDefinition = [{"constant":true,"inputs":[{"name":"_address","type":"address"}],"name":"getIssuerName","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_address","type":"address"}],"name":"getStudentName","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getIssuerCertificates","outputs":[{"name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"listStudentAddresses","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_address","type":"address"}],"name":"getIssuerDetails","outputs":[{"name":"","type":"string"},{"name":"","type":"string"},{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"student","type":"address"},{"name":"courseTitle","type":"string"},{"name":"completionDate","type":"string"},{"name":"expiryDate","type":"string"}],"name":"createCertificate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"index","type":"uint256"}],"name":"getCertificateDetails","outputs":[{"name":"","type":"string"},{"name":"","type":"string"},{"name":"","type":"string"},{"name":"","type":"string"},{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_address","type":"address"},{"name":"_name","type":"string"},{"name":"_issuerAddress","type":"string"},{"name":"_logoUrl","type":"string"}],"name":"createIssuer","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_address","type":"address"},{"name":"_name","type":"string"}],"name":"createStudent","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"listIssuerAddresses","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getStudentCertificates","outputs":[{"name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"student","type":"address"},{"name":"issuer","type":"address"},{"name":"courseTitle","type":"string"}],"name":"verifyCertificate","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]; + var contractAddress = '0x64ec4b719fa6b1fb835b7212e1973236be746989'; + var masterContract = web3.eth.contract(abiDefinition); + var contractInstance = masterContract.at(contractAddress); + + $('form').submit(function (event) { + $('.alert-danger, .alert-success').hide(); + var address = $("#address").val(); + var issuerName = $("#issuer-name").val(); + var issuerAddress = $("#issuer-address").val(); + var url = $("#logo-url").val(); + var index = web3.eth.accounts.indexOf(address); + + if (index < 0) { + $('.alert-danger').html('Account address not found.').show(); + console.log('Valid addresses', web3.eth.accounts); + } else { + try { + var estimatedGas = contractInstance.createIssuer.estimateGas(address, issuerName, issuerAddress, url); + contractInstance.createIssuer(address, issuerName, issuerAddress, url, {from: account, gas: estimatedGas}); + $('.alert-success').show(); + $('#address, #issuer-name, #issuer-address, #logo-url').val(''); + } catch (e) { + console.log(e); + $('.alert-danger').html('Error occurred.').show(); + } + } + + event.preventDefault(); + }); + } else { + $('form').prepend('
Blockchain not connected
'); + $(':submit').prop('disabled', true); + } +}); \ No newline at end of file diff --git a/js/search.js b/js/search.js new file mode 100644 index 0000000..da91789 --- /dev/null +++ b/js/search.js @@ -0,0 +1,47 @@ +$(document).ready(function() { + + var web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); + + if (web3.isConnected()) { + + console.log('Web3 successfully connected'); + var abiDefinition = [{"constant":true,"inputs":[{"name":"_address","type":"address"}],"name":"getIssuerName","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_address","type":"address"}],"name":"getStudentName","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getIssuerCertificates","outputs":[{"name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"listStudentAddresses","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_address","type":"address"}],"name":"getIssuerDetails","outputs":[{"name":"","type":"string"},{"name":"","type":"string"},{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"student","type":"address"},{"name":"courseTitle","type":"string"},{"name":"completionDate","type":"string"},{"name":"expiryDate","type":"string"}],"name":"createCertificate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"index","type":"uint256"}],"name":"getCertificateDetails","outputs":[{"name":"","type":"string"},{"name":"","type":"string"},{"name":"","type":"string"},{"name":"","type":"string"},{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_address","type":"address"},{"name":"_name","type":"string"},{"name":"_issuerAddress","type":"string"},{"name":"_logoUrl","type":"string"}],"name":"createIssuer","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_address","type":"address"},{"name":"_name","type":"string"}],"name":"createStudent","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"listIssuerAddresses","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getStudentCertificates","outputs":[{"name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"student","type":"address"},{"name":"issuer","type":"address"},{"name":"courseTitle","type":"string"}],"name":"verifyCertificate","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]; + var contractAddress = '0x64ec4b719fa6b1fb835b7212e1973236be746989'; + var masterContract = web3.eth.contract(abiDefinition); + var contractInstance = masterContract.at(contractAddress); + + $('form').submit(function (event) { + + $('.alert-warning, .alert-success').hide(); + var student = $("#student").val(); + var issuer = $("#issuer").val(); + var title = $("#title").val(); + var studentIndex = web3.eth.accounts.indexOf(student); + var issuerIndex = web3.eth.accounts.indexOf(issuer); + + if (studentIndex < 0 || issuerIndex < 0) { + $('.alert-danger').html('Account address not found.').show(); + console.log('Valid addresses', web3.eth.accounts); + } else { + // Search the certificate + var index = contractInstance.verifyCertificate(student, issuer, title); + if (index > 0) { + $('.alert-success').show(); + $("#student, #issuer, #title").val(''); + var certificateDetails = contractInstance.getCertificateDetails(index - 1); + $('#issuer-name').text(certificateDetails[0]); + $('#student-name').text(certificateDetails[1]); + $('#course-title').text(certificateDetails[2]); + $('#completion-date').text(certificateDetails[3]); + } else { + $('.alert-warning').show(); + } + } + + event.preventDefault(); + }); + } else { + $('form').prepend('
Blockchain not connected
'); + $(':submit').prop('disabled', true); + } +}); \ No newline at end of file diff --git a/js/student-certificates.js b/js/student-certificates.js new file mode 100644 index 0000000..5795896 --- /dev/null +++ b/js/student-certificates.js @@ -0,0 +1,49 @@ +$(document).ready(function() { + + var web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); + + if (web3.isConnected()) { + + console.log('Web3 successfully connected'); + var studentAccount = web3.eth.accounts[2]; + var abiDefinition = [{"constant":true,"inputs":[{"name":"_address","type":"address"}],"name":"getIssuerName","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_address","type":"address"}],"name":"getStudentName","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getIssuerCertificates","outputs":[{"name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"listStudentAddresses","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_address","type":"address"}],"name":"getIssuerDetails","outputs":[{"name":"","type":"string"},{"name":"","type":"string"},{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"student","type":"address"},{"name":"courseTitle","type":"string"},{"name":"completionDate","type":"string"},{"name":"expiryDate","type":"string"}],"name":"createCertificate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"index","type":"uint256"}],"name":"getCertificateDetails","outputs":[{"name":"","type":"string"},{"name":"","type":"string"},{"name":"","type":"string"},{"name":"","type":"string"},{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_address","type":"address"},{"name":"_name","type":"string"},{"name":"_issuerAddress","type":"string"},{"name":"_logoUrl","type":"string"}],"name":"createIssuer","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_address","type":"address"},{"name":"_name","type":"string"}],"name":"createStudent","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"listIssuerAddresses","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getStudentCertificates","outputs":[{"name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"student","type":"address"},{"name":"issuer","type":"address"},{"name":"courseTitle","type":"string"}],"name":"verifyCertificate","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]; + var contractAddress = '0x64ec4b719fa6b1fb835b7212e1973236be746989'; + var masterContract = web3.eth.contract(abiDefinition); + var contractInstance = masterContract.at(contractAddress); + web3.eth.defaultAccount = studentAccount; + + try { + var studentName = contractInstance.getStudentName(studentAccount); + $('h1').html('My Certificates - ' + studentName); + $('#student-name').text(studentName); + } catch (e) { + console.log(e); + $('table').replaceWith('
Student not found
'); + } + + var certificates = contractInstance.getStudentCertificates(); + console.log(certificates.length + " certificate record(s) loaded"); + + if (certificates.length > 0) { + certificates.forEach(function (index) { + var certificateDetails = contractInstance.getCertificateDetails(index); + var expiry = certificateDetails[4] ? certificateDetails[4] : "N/A"; + var issuer = '' + certificateDetails[0] + ''; + $('table tbody').append('' + issuer + '' + certificateDetails[2] + '' + certificateDetails[3] + '' + expiry + ''); + $('table').show(); + }); + } else { + $('table').replaceWith('
You have no certificates.
'); + } + $('table').on('click', '.cert-button', function () { + var issuer = $(this).text(); + var courseTitle = $(this).parent().next(); + var completionDate = courseTitle.next(); + $('#issuer-name').text(issuer); + $('#course-title').text(courseTitle.text()); + $('#completion-date').text(completionDate.text()); + }); + } else { + $('table').replaceWith('
Blockchain not connected
'); + } +}); \ No newline at end of file diff --git a/js/student-list.js b/js/student-list.js new file mode 100644 index 0000000..99cf021 --- /dev/null +++ b/js/student-list.js @@ -0,0 +1,30 @@ +$(document).ready(function() { + + var web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); + + if (web3.isConnected()) { + + console.log('Web3 successfully connected'); + var abiDefinition = [{"constant":true,"inputs":[{"name":"_address","type":"address"}],"name":"getIssuerName","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_address","type":"address"}],"name":"getStudentName","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getIssuerCertificates","outputs":[{"name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"listStudentAddresses","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_address","type":"address"}],"name":"getIssuerDetails","outputs":[{"name":"","type":"string"},{"name":"","type":"string"},{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"student","type":"address"},{"name":"courseTitle","type":"string"},{"name":"completionDate","type":"string"},{"name":"expiryDate","type":"string"}],"name":"createCertificate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"index","type":"uint256"}],"name":"getCertificateDetails","outputs":[{"name":"","type":"string"},{"name":"","type":"string"},{"name":"","type":"string"},{"name":"","type":"string"},{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_address","type":"address"},{"name":"_name","type":"string"},{"name":"_issuerAddress","type":"string"},{"name":"_logoUrl","type":"string"}],"name":"createIssuer","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_address","type":"address"},{"name":"_name","type":"string"}],"name":"createStudent","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"listIssuerAddresses","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getStudentCertificates","outputs":[{"name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"student","type":"address"},{"name":"issuer","type":"address"},{"name":"courseTitle","type":"string"}],"name":"verifyCertificate","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]; + var contractAddress = '0x64ec4b719fa6b1fb835b7212e1973236be746989'; + var masterContract = web3.eth.contract(abiDefinition); + var contractInstance = masterContract.at(contractAddress); + var studentAddresses = contractInstance.listStudentAddresses(); + console.log(studentAddresses.length + ' student record(s) loaded'); + + if (studentAddresses.length > 0) { + studentAddresses.forEach(function (address) { + // Query the student name from blockchain + var studentName = contractInstance.getStudentName(address); + + // Append a new HTML table row + $('table tbody').append('' + studentName + '' + address + ''); + $('table').show(); + }); + } else { + $('table').replaceWith('
No student accounts in the system
'); + } + } else { + $('table').replaceWith('
Blockchain not connected
'); + } +}); \ No newline at end of file diff --git a/js/student.js b/js/student.js new file mode 100644 index 0000000..a187308 --- /dev/null +++ b/js/student.js @@ -0,0 +1,43 @@ +$(document).ready(function() { + + var web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); + + if (web3.isConnected()) { + + console.log('Web3 successfully connected'); + var account = web3.eth.accounts[0]; + web3.personal.unlockAccount(account); + + var abiDefinition = [{"constant":true,"inputs":[{"name":"_address","type":"address"}],"name":"getIssuerName","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_address","type":"address"}],"name":"getStudentName","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getIssuerCertificates","outputs":[{"name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"listStudentAddresses","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_address","type":"address"}],"name":"getIssuerDetails","outputs":[{"name":"","type":"string"},{"name":"","type":"string"},{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"student","type":"address"},{"name":"courseTitle","type":"string"},{"name":"completionDate","type":"string"},{"name":"expiryDate","type":"string"}],"name":"createCertificate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"index","type":"uint256"}],"name":"getCertificateDetails","outputs":[{"name":"","type":"string"},{"name":"","type":"string"},{"name":"","type":"string"},{"name":"","type":"string"},{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_address","type":"address"},{"name":"_name","type":"string"},{"name":"_issuerAddress","type":"string"},{"name":"_logoUrl","type":"string"}],"name":"createIssuer","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_address","type":"address"},{"name":"_name","type":"string"}],"name":"createStudent","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"listIssuerAddresses","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getStudentCertificates","outputs":[{"name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"student","type":"address"},{"name":"issuer","type":"address"},{"name":"courseTitle","type":"string"}],"name":"verifyCertificate","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]; + var contractAddress = '0x64ec4b719fa6b1fb835b7212e1973236be746989'; + var masterContract = web3.eth.contract(abiDefinition); + var contractInstance = masterContract.at(contractAddress); + + $('form').submit(function (event) { + $('.alert-danger, .alert-success').hide(); + var address = $("#address").val(); + var fullName = $("#full-name").val(); + var index = web3.eth.accounts.indexOf(address); + + if (index < 0) { + $('.alert-danger').html('Account address not found.').show(); + console.log('Valid addresses', web3.eth.accounts); + } else { + try { + var estimatedGas = contractInstance.createStudent.estimateGas(address, fullName); + contractInstance.createStudent(address, fullName, {from: account, gas: estimatedGas}); + $('.alert-success').show(); + $('#address, #full-name').val(''); + } catch (e) { + console.log(e); + $('.alert-danger').html('Error occurred.').show(); + } + } + + event.preventDefault(); + }); + } else { + $('form').prepend('
Blockchain not connected
'); + $(':submit').prop('disabled', true); + } +}); \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..56a68be --- /dev/null +++ b/package.json @@ -0,0 +1,19 @@ +{ + "name": "certificate-blockchain", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "dev": "lite-server" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "lite-server": "^2.3.0" + }, + "dependencies": { + "ganache-cli": "^6.1.3", + "solc": "^0.4.24", + "web3": "^0.20.2" + } +} diff --git a/search.html b/search.html new file mode 100644 index 0000000..8035656 --- /dev/null +++ b/search.html @@ -0,0 +1,56 @@ + + + + Verify Certificate + + + + + + + + + + +
+ +
+ +
+
+ +
+
+ +
+ + + Back +
+ + + \ No newline at end of file diff --git a/student-certificates.html b/student-certificates.html new file mode 100644 index 0000000..beef27f --- /dev/null +++ b/student-certificates.html @@ -0,0 +1,56 @@ + + + + My Certificates + + + + + + + + + + +
+ + + + + + + + + + + + +
+ Back + + + \ No newline at end of file diff --git a/student-list.html b/student-list.html new file mode 100644 index 0000000..cafc001 --- /dev/null +++ b/student-list.html @@ -0,0 +1,31 @@ + + + + Student Listing + + + + + + +
+ + + + + + + + + + +
+ Create Student + Back + + + + + \ No newline at end of file diff --git a/student.html b/student.html new file mode 100644 index 0000000..858375f --- /dev/null +++ b/student.html @@ -0,0 +1,32 @@ + + + + Create Student + + + + + + +
+ +
+ +
+
+ +
+ + + Cancel +
+ + + + + \ No newline at end of file