Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
const PassportManager = artifacts.require("PassportManager");
contract("Bluechain SSI PassportManager test-suite Alpha", accounts => {
it("should initialize a new passport linked with the current user's address", () => {
let this_address = accounts[0];
let this_nickname = "John Doe";
let meta;
return PassportManager.deployed()
.then(instance => {
meta = instance;
console.log("Test1 on PassportManager address: " + meta.address);
return meta.initPassport.call(this_nickname);
})
.then(returned_tuple => {
//console.log(returned_tuple[0]);
//console.log(returned_tuple[1]);
assert.equal(returned_tuple[0], this_nickname, "Nickname, passed and returned by PassportManager.initPassport(), should match!");
assert.equal(returned_tuple[1], this_address, "Controller address, passed and returned by PassportManager.initPassport(), should match!");
//If we're here, it means the previous call() has succeeded. Now,
//let's take things up a notch and let's actually send a transaction that changes the state of the blockchain.
//Remember: We can't check for return values with a transaction, we have to debug the tx id manually.
//#NOTE: We passed an extra parameter here. For more info on this special parameter object, check out:
//https://www.trufflesuite.com/docs/truffle/getting-started/interacting-with-your-contracts#making-a-transaction
const result = meta.initPassport.sendTransaction(this_nickname, {from: accounts[0]});
result.on('transactionHash', (hash) => {
console.log('TxHash', hash);
});
return result;
})
.then(result => {
//#NOTE: This is a hacky solution at ensuring Ganache has had time to process the tx.
console.log("Test 1 should've succeeded!");
});
});
it("should successfully initialize a 2nd passport linked with accounts[1].", () => {
let this_address = accounts[1];
let this_nickname = "Theocharis Iordanidis";
let meta;
return PassportManager.deployed()
.then(instance => {
meta = instance;
console.log("Test2 on PassportManager address: " + meta.address);
return meta.initPassport.call(this_nickname, {from: accounts[1]});
})
.then(returned_tuple => {
//console.log(returned_tuple[0]);
//console.log(returned_tuple[1]);
assert.equal(returned_tuple[0], this_nickname, "Nickname, passed and returned by PassportManager.initPassport(), should match!");
assert.equal(returned_tuple[1], this_address, "Controller address, passed and returned by PassportManager.initPassport(), should match!");
//If we're here, it means the previous call() has succeeded. Now,
//let's take things up a notch and let's actually send a transaction that changes the state of the blockchain.
//Remember: We can't check for return values with a transaction, we have to debug the tx id manually.
//#NOTE: We passed an extra parameter here. For more info on this special parameter object, check out:
//https://www.trufflesuite.com/docs/truffle/getting-started/interacting-with-your-contracts#making-a-transaction
const result = meta.initPassport.sendTransaction(this_nickname, {from: accounts[1]});
result.on('transactionHash', (hash) => {
console.log('TxHash', hash);
});
return result;
})
.then(result => {
//#NOTE: This is a hacky solution at ensuring Ganache has had time to process the tx.
console.log("Test 2 should've succeeded!");
});
});
it("should add an identity file sha256 hash to a controlled passport", () => {
let this_address = accounts[0];
let doc_hash = "0x21f3a9de43f07d855f49b946a10c30df432e8af95311435f77daf894216dcd41";
let meta;
return PassportManager.deployed()
.then(instance => {
meta = instance;
console.log("Test3 on PassportManager address: " + meta.address);
return meta.addIDFileToPassport.call(this_address, doc_hash);
})
.then(returned_tuple => {
assert.equal(returned_tuple[0], this_address, "Passport controller, passed and returned by PassportManager.addIDFileToPassport(), should match!");
assert.equal(returned_tuple[1], doc_hash, "Document hash (bytes32), passed and returned by PassportManager.addIDFileToPassport(), should match!");
assert.equal(returned_tuple[2], 1, "Trust score of newly added doc_hash should be 1!");
console.log(returned_tuple);
//Now let's actually pass a concrete, actuallly persistent transaction instead of a call.
const result = meta.addIDFileToPassport.sendTransaction(this_address, doc_hash, {from: accounts[0]});
result.on('transactionHash', (hash) => {
console.log('TxHash', hash);
});
return result;
})
.then(result => {
//#NOTE: This is a hacky solution at ensuring Ganache has had time to process the tx.
console.log("Test 3 should've succeeded!");
});
});
it("should add +1 to the trust score of a doc in a passport", () => {
let this_address = accounts[1];
let passport_address = accounts[0];
let doc_hash = "0x21f3a9de43f07d855f49b946a10c30df432e8af95311435f77daf894216dcd41";
let meta;
return PassportManager.deployed()
.then(instance => {
meta = instance;
console.log("Test4 on PassportManager address: " + meta.address);
return meta.voteForDocInPassport.call(passport_address, doc_hash, {from: this_address});
})
.then(returned_tuple => {
assert.equal(returned_tuple[0], passport_address, "Passport ID address, passed and returned by PassportManager.voteForDocInPassport(), should match!");
assert.equal(returned_tuple[1], doc_hash, "Document hash (bytes32), passed and returned by PassportManager.voteForDocInPassport(), should match!");
assert.equal(returned_tuple[2], 2, "Trust score of doc_id should have been raised to 2!");
console.log(returned_tuple);
//Now let's actually pass a concrete, actuallly persistent transaction instead of a call.
const result = meta.voteForDocInPassport.sendTransaction(passport_address, doc_hash, {from: this_address});
result.on('transactionHash', (hash) => {
console.log('TxHash', hash);
});
return result;
})
.then(result => {
//#NOTE: This is a hacky solution at ensuring Ganache has had time to process the tx.
console.log("Test 4 should've succeeded!");
});
});
/*it("return false", () => {
assert(0==1);
});
*/
});$ truffle testUsing network 'development'.
Compiling your contracts...
===========================
> Everything is up to date, there is nothing to compile.
PassportManager contract deployed at address: 0xC6DAa380707E13829700EF40Ad7fE6f2A7297FcF
Contract: PassportManager testsuite Alpha
Test1 on PassportManager address: 0xC6DAa380707E13829700EF40Ad7fE6f2A7297FcF
TxHash 0x550509d1fea429e23c56745d14f6790bcc5b2389dd3a55aaf85303d1ded33d05
Test 1 should've succeeded!
✓ should initialize a new passport linked with the current user's address (404ms)
Test2 on PassportManager address: 0xC6DAa380707E13829700EF40Ad7fE6f2A7297FcF
TxHash 0x14cfae455ddb0d6161374f43f84e72df5ba3e10e42068e0a0d150f66390df2b8
Test 2 should've succeeded!
✓ should successfully initialize a 2nd passport linked with accounts[1]. (223ms)
Test3 on PassportManager address: 0xC6DAa380707E13829700EF40Ad7fE6f2A7297FcF
Result {
'0': '0x4c8C46E0269C7882A2427eF282a426eBb9716767',
'1': '0x21f3a9de43f07d855f49b946a10c30df432e8af95311435f77daf894216dcd41',
'2': BN {
negative: 0,
words: [ 1, <1 empty item> ],
length: 1,
red: null
}
}
TxHash 0x013ae2f108056fee7d2bc5882ea63e7a6b5f923925060029b495aad0894f9d74
Test 3 should've succeeded!
✓ should add an identity file sha256 hash to a controlled passport (210ms)
Test4 on PassportManager address: 0xC6DAa380707E13829700EF40Ad7fE6f2A7297FcF
Result {
'0': '0x4c8C46E0269C7882A2427eF282a426eBb9716767',
'1': '0x21f3a9de43f07d855f49b946a10c30df432e8af95311435f77daf894216dcd41',
'2': BN {
negative: 0,
words: [ 2, <1 empty item> ],
length: 1,
red: null
}
}
TxHash 0x6b5031a08041859f969da7babbae59747d5bf997b00c1724920f3fdc0e3fb9e9
Test 4 should've succeeded!
✓ should add +1 to the trust score of a doc in a passport (158ms)
4 passing (1s)$ chmod +x ganache-2.5.4-linux-x86_64.AppImage$ ./ganache-2.5.4-linux-x86_64.AppImagemodule.exports = {
/*
* Εφιστούμε την προσοχή μας εδώ!
*/
networks: {
development: {
host: "127.0.0.1", // Localhost (default: none)
port: 7545, // Standard Ethereum port (default: none)
network_id: "5777", // Any network (default: none)
},
},
mocha: {
},
compilers: {
solc: {
version: "0.8.2", // Fetch exact version from solc-bin (default: truffle's version)
}
}
}$ truffle compile --all
$ truffle migrate --reset$ truffle compile --all
Compiling your contracts...
===========================
> Compiling ./contracts/Migrations.sol
> Compiling ./contracts/PassportManager.sol
> Artifacts written to /home/dimitris/Documents/Code/Bluechain_SSI_Truffle/bluechain_ssi/build/contracts
> Compiled successfully using:
- solc: 0.8.2+commit.661d1103.Emscripten.clang
$ truffle migrate --reset
Compiling your contracts...
===========================
> Everything is up to date, there is nothing to compile.
Starting migrations...
======================
> Network name: 'development'
> Network id: 5777
> Block gas limit: 6721975 (0x6691b7)
1_initial_migration.js
======================
Replacing 'Migrations'
----------------------
> transaction hash: 0x4c95c2ef66121f10b67aed6429f8124527f6cba7fa99a321c7f00a6f5da0f605
> Blocks: 0 Seconds: 0
> contract address: 0x4dEcd29fc37dd3124b30f7412e9a16B910cfd79f
> block number: 995
> block timestamp: 1620854844
> account: 0x4c8C46E0269C7882A2427eF282a426eBb9716767
> balance: 95.11712696
> gas used: 246904 (0x3c478)
> gas price: 20 gwei
> value sent: 0 ETH
> total cost: 0.00493808 ETH
> Saving migration to chain.
> Saving artifacts
-------------------------------------
> Total cost: 0.00493808 ETH
2_deploy_contracts.js
=====================
Replacing 'PassportManager'
---------------------------
> transaction hash: 0x48f73ad6d3ea9537dcdaaaeeaa6aec4d17b068056340bf75c5009ba5d4838a92
> Blocks: 0 Seconds: 0
> contract address: 0xCcd04bB0b4beb0A544311c6ce1cE324Ff6c049EE
> block number: 997
> block timestamp: 1620854845
> account: 0x4c8C46E0269C7882A2427eF282a426eBb9716767
> balance: 95.09212666
> gas used: 1207502 (0x126cce)
> gas price: 20 gwei
> value sent: 0 ETH
> total cost: 0.02415004 ETH
PassportManager contract deployed at address: 0xCcd04bB0b4beb0A544311c6ce1cE324Ff6c049EE
> Saving migration to chain.
> Saving artifacts
-------------------------------------
> Total cost: 0.02415004 ETH
Summary
=======
> Total deployments: 2
> Final cost: 0.02908812 ETH
Υπεύθυνοι για την ανάπτυξη του λογισμικού και του κώδικα στο παρόν έργο.
Μία-προς-μία όλες οι μεταβλητές και λειτουργίες του συστήματος Bluechain SSI, και τεκμηρίωση.
mapping(bytes32 => uint) identity_files: Ένα hashmap array που περιέχει όλα τα σχετικά με το συγκεκριμένο Διαβατήριο Έγγραφα Ταυτοποίησης. Το (key, value) ζευγάρι σε κάθε θέση είναι μορφής bytes32 (32 byte string, επίτηδες έτσι ώστε να μπορεί να φιλοξενήσει SoliditySha3()output του Web3 library, keccak256 outputs, ή ακόμα και απλά sha3-256 checksums ενός εγγράφου) σε uint (unsigned integer, όπου αποθηκεύεται ως ένας απλός ακέραιος αριθμός το trust score ενός εγγράφου. git clone https://github.com/d-sfounis/bluechain_SSI.gitgit clone git@github.com:d-sfounis/bluechain_SSI.git/* SPDX-License-Identifier: GPL-3.0-or-later
* Author: Dimitrios Sfounis, for Bluechain Social Cooperative Enterprise, Athens (GR), February 2021
* Website: https://bluechain.tech
* Project: European Self-Sovereign Identity on blockchain, intended for electronic Health records,
* Funded under the European Cohesion Fund's "Vouchers for Innovation" programme.
*/
pragma solidity >=0.8.0 <0.9;
contract PassportManager {
/* Basic implementation of a user's identity: a Passport! */
struct Passport {
string flair_name; //User nickname. It's optional.
//The controller (owner) of this passport instance.
//Initially, it's the first user/deployer. This can change later on.
address controller;
//the main workhorse of this struct. This is a hashmap of
//key-value pairs of keccak256 hashes of the (string)sha256 checksum of any identity file (say, a PDF certificate)
//and a trust-score stored as a simple integer. The more people have voted to trust this particular document of your passport,
//the higher the score is. Simple, right? Probably not...
//bytes32 because keccak256, through the web3.eth lib, always produces 32 bytes (256 bits) of output.
mapping(bytes32 => uint) identity_files;
//Lookup table for the previous mapping, just so we don't ever have to deal with orphaned values.
//#NOTE: If you self-destruct this contract, be sure to iterate over `identity_files` and delete all keys using this Lookup Table.
mapping(bytes32 => bytes32) identity_files_LUT;
//Also this helps for easy, O(1) search of whether an address is here or not: //#NOTE: DEPRECATED in v0.2.0
//mapping(bytes32 => bool) identity_files_LUT_Bool;
//Delegates of this passport and its contained identity files, able to control it too.
//Disabling this for now, we'll see if we can circumvent delegation functionality as defined in the CALYSPSO paper by Kokkoris-Kogias.
//address[] delegates;
}
//The main database of our user IDs. This hashtable stores key-value pairs of
//address to (struct Passport) objects belonging to this smart contract's users.
//bytes32 because keccak256, through the web3 library, always produces 32 bytes (256 bits) of output.
//the public attribute just gives it an automatic getter. However, the getter only returns a tuple containing all atomic fields,
//and not the actual data structure.
mapping(address => Passport) public user_passports;
//And the associated lookup table, for quick O(1) lookup:
mapping(address => address) public user_passports_LUT;
/* Debugging Events! Respect these, they'll save you headache(s) */
event PassportInitialized(address passport_id, address by);
event AddedIDFileToPassport(address passport_id, bytes32 hashed_file);
event Voted(address passport_id, address voter);
/* Helper function to check if a user has initialized/created a Passport before */
function hasInitializedPassport(address addr) private view returns (bool) {
//All these checks cumulatively mean that a passport has been properly initialized
if(user_passports_LUT[addr] != address(0)){
Passport storage p = user_passports[addr];
if(p.controller != address(0)){
return true;
}
}
return false;
}
function initPassport(string memory nickname) public returns (string memory, address) {
require(!(hasInitializedPassport(msg.sender)), "Your User Passport is already initialized!");
//As taken by the Solidity docs:
//We cannot use "Passport[campaignID] = Passport(beneficiary, goal, 0, 0)"
//because the RHS creates a memory-struct "Passport" that contains a mapping.
Passport storage p = user_passports[msg.sender];
p.flair_name = nickname;
p.controller = msg.sender;
//Mark passport as initialized, and emit the event.
user_passports_LUT[msg.sender] = msg.sender;
emit PassportInitialized(user_passports_LUT[msg.sender], msg.sender);
return (p.flair_name, p.controller);
}
function addIDFileToPassport(address passport_id, bytes32 id_file) public returns (address, bytes32, uint) {
Passport storage p = user_passports[passport_id];
require(hasInitializedPassport(passport_id));
require(p.controller == msg.sender, "Sender/controller mismatch when accessing passport");
require(p.identity_files_LUT[id_file] == "", "ID File is already contained in this Passport!");
p.identity_files_LUT[id_file] = id_file;
//trust-score == 1 means we've self-added this file and only us do trust it. In other words, this is a new, self-trusted-only doc.
//#TODO: There's probably a better way to do this other than using magic numbers...
p.identity_files[id_file] = 1;
//Return all these for debugging and testing purposes. It's a tuple of (controller_address, identity_file_byte32hash, trust_score).
emit AddedIDFileToPassport(passport_id, id_file);
return (p.controller, p.identity_files_LUT[id_file], p.identity_files[id_file]);
}
//This function raises the trust score of a Passport, when a user vouches for it.
//Only users with already initiated Passports, and therefore active members of the community, can vouch for other people's Passports.
function voteForDocInPassport(address passport_id, bytes32 doc_id) public returns (address, bytes32, uint) {
require(PassportExists(passport_id), "Passport_id does not exist!");
require(passport_id != msg.sender, "You can't vote on the trust score of your own Passport's documents!");
Passport storage p = user_passports[passport_id];
require(DocExistsInPassport(p, doc_id), "Document doesn't exist in specified Passport!");
require(hasInitializedPassport(msg.sender), "You have to have an active Passport yourself to vote on others!");
//Everything ok, we found it. Raise its score by 1.
p.identity_files[doc_id] = p.identity_files[doc_id] + 1;
return (passport_id, doc_id, p.identity_files[doc_id]);
}
function PassportExists(address passport_id) private view returns (bool) {
if(user_passports_LUT[passport_id] == address(0)){
return false;
}
return true;
}
function DocExistsInPassport(Passport storage P, bytes32 doc_id) private view returns (bool) {
if(P.identity_files_LUT[doc_id] == bytes32(0)){
return false;
}
return true;
}
/* TODO 1: We need a voting function, to play with trust_scores of stored Passport documents */
/* DONE! */
/* TODO 2: We need a function to export DID documents and Verifiable Claims as per specification format. Possibly a view function, so it doesn't burn up gas */
/* TODO 3: We need some failsafe against Orphaned Passports... */
/* TODO 4: Currently, trust_scores of documents are handled like magic numbers. This is generally an anti-pattern, let's look into a better way of doing this. */
/* TODO 5: The contract should maintain a list of Arbitrator addresses, for example Gnomon's address. Votes to documents from those addresses should give +100 score instead of just +1 */
/* This is as per the CALYPSO paper where the Trust Committee is maintained as a separate group, and votes from them are stronger */
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity >=0.4.22 <0.9.0;
contract Migrations {
address public owner = msg.sender;
uint public last_completed_migration;
modifier restricted() {
require(
msg.sender == owner,
"This function is restricted to the contract's owner"
);
_;
}
function setCompleted(uint completed) public restricted {
last_completed_migration = completed;
}
}