NodeJS (v10.x++) https://nodejs.org/en/
npm (v7.x++) https://www.npmjs.com/get-npm
Solidity compiler (v0.8.x++) https://docs.soliditylang.org/en/v0.8.4/installing-solidity.html
Truffle Development Environment https://www.trufflesuite.com/truffle https://github.com/trufflesuite/truffle
Ganache for Ethereum https://www.trufflesuite.com/ganache https://github.com/trufflesuite/ganache
Το έργο μας, σε όλες τις εκδόσεις του και ακόμα κι όσο αναπτύσσεται, διατηρείται στο εξής εναποθετήριο (repository): https://github.com/d-sfounis/bluechain_SSI Είναι λογισμικό Ανοικτού Κώδικα (Open Source), συμμορφώνεται στα πρότυπα της Ανοικτά Διαθέσιμης Επιστήμης (Open Science) κατά ΕΕ, και παραμένει και διαθέσιμο προς κάθε Πολίτη.
Η τελευταία έκδοση του συστήματος Bluechain SSI είναι η v0.3.0 (Μάιος 2021).
Για το local git cloning του λογισμικού, χρησιμοποιείστε είτε το ίδιο το GUI της πλατφόρμας του GitHub, είτε μέσω HTTPS, είτε μέσω SSH:
git clone https://github.com/d-sfounis/bluechain_SSI.gitgit clone git@github.com:d-sfounis/bluechain_SSI.gitΤο Bluechain SSI χρειάζεται να συνδεθεί σε ένα "ζωντανό" Ethereum blockchain network, αφού πάνω σε αυτό λειτουργεί.
Η πιο εύκολη λύση για αυτό είναι ένα local Ethereum network μέσω Ganache (το εγκαταστήσαμε στο προηγούμενο βήμα - Εγκατάσταση και Xρήση του Bluechain SSI). Το Ganache μπορεί να ρυθμιστεί ώστε να κοιτάει σε local ETH network (φτιαγμένο ως ειδικό instance αποκλειστικά και μόνο στο μηχάνημά σας), ή σε live ETH networks (Ethereum Mainnet, Testnets Ropsten, Koeva, Rinkeby, Goerli) ή ακόμα και το Binance Smart Network (αφού αυτό είναι 1:1 clone του Ethereum).
Συστήνουμε: Local Ethereum instance ή Ropsten testnet. Το σύστημα Bluechain Identity ΔΕΝ έχει βελτιστοποιηθεί ως προς την οικονομική του λειτουργία ώστε να μπορούμε να προτείνουμε την χρήση σε mainnet.
Έχοντας λοιπόν το .AppImage του Ganache κατεβασμένο:
$ chmod +x ganache-2.5.4-linux-x86_64.AppImageΈπειτα εκκινήστε το Ganache απλά με:
$ ./ganache-2.5.4-linux-x86_64.AppImageΔημιουργήστε ένα νέο blockchain workspace τύπου Ethereum μέσω του δεξιού βέλους (το Bluechain SSI δεν είναι ακόμη συμβατό με R3 Corda, κατά την τωρινή του υλοποίηση (v0.2):
To Truffle αποτελεί το development environment του Bluechain SSI. Παρέχει συγκεντρωτικά: compiler, debugger, logging console και εύκολα/γρήγορα configs, όλα μαζί. Επίσης παρέχει ένα βασικό virtual blockchain πάνω στο οποίο μπορούμε να τρέξουμε tests και migrations (deployments, αλλά το truffle επιλέγει να τα ονομάζει έτσι). ΔΕΝ χρησιμοποιούμε το virtual development blockchain που εμπεριέχει το truffle, αφού έχουμε το Ganache το οποίο προσφέρει ένα καλύτερο και ρεαλιστικότερο περιβάλλον μετρήσεων και performance testing.
Αρχικά, πρέπει να σιγουρέψουμε ότι το Truffle κοιτάει προς το Ganache local blockchain μας. Σε ενεργό Ganache, για να δούμε από που "ακούει" ο server: ⚙️ Config → SERVER tab, και βλέπουμε το configuration.
Έπειτα, στο home directory του κατεβασμένου (cloned) Bluechain SSI, σετάρουμε το truffle-config.js:
module.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)
}
}
}Όλο αυτό αποτελεί ένα πολύ bare bones setup, αλλά μας είναι αρκετό προς το παρόν.
Στη συνέχεια, πάλι στο home directory του project και με ανοικτό το Ganache, τρέχουμε:
$ truffle compile --all
$ truffle migrate --resetΌλα έτοιμα! Αν όλα πήγαν καλά, το Truffle έκανε deploy το Smart Contract σύστημα του Bluechain SSI, και είναι έτοιμο προς χρήση: Να δεχθεί requests, να δεχθεί διαβατήρια και έγγραφα χρηστών, να επαληθεύσει την εγκυρότητα υπαρχόντων εγγράφων και Ισχυρισμών (Claims).
$ 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 συνοδεύεται από μια πλειάδα unit tests για να επιτυγχάνεται η διαρκής ορθότητα του κώδικα. Σκοπός των unit tests είναι οι βασικές λειτουργίες του συστήματος να συνεχίζουν να λειτουργούν ορθά και με τον ίδιο τρόπο, καθ'όσο αυτό αναπτύσσεται και επεκτείνεται πέραν της έκδοσης 0.3.0 (όπου βρισκόμαστε τώρα).
Συμπεριλαμβάνουμε 4 unit tests (μορφής it()) σε μορφή Javascript Promise.
Καθένα από αυτά, έχει φτιαχτεί για να ελέγχει ενδελεχώς μία εκ των 4 βασικών λειτουργιών του Bluechain (βλέπε επόμενη ενότητα - 📕 Αναλυτική λίστα μεθόδων & μεταβλητών (Reference))
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);
});
*/
});Τρέξτε όλα τα παρόντα tests πολύ απλά, με την εντολή:
$ truffle testΚαι αναμένουμε ως output τα αποτελέσματα των validity & performance tests:
Using 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)Η αποδοτικότητα σε χρόνο των συναλλαγών του Bluechain SSI κυμαίνεται γύρω στα ~250milliseconds και εμφανίζει ολικά ακρότατα των 425ms (κατά τις πιο "βαριές" λειτουργίες δημιουργίας νέας Ταυτότητας Χρήστη ή εισαγωγής νέων εγγράφων πιστοποίησης).