Only this pageAll pages
Powered by GitBook
1 of 7

Bluechain Identity - Εγχειρίδιο Χρήσης

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

🏗️ Εγκατάσταση & Χρήση του Bluechain SSI

Α. Σύνολο εργαλείων που απαιτούνται (development stack)

B. Τοπική εγκατάσταση (git cloning) του Bluechain Identity

Η τελευταία έκδοση του συστήματος Bluechain SSI είναι η v0.3.0 (Μάιος 2021).

Για το local git cloning του λογισμικού, χρησιμοποιείστε είτε το ίδιο το GUI της πλατφόρμας του GitHub, είτε μέσω HTTPS, είτε μέσω SSH:

B1. Mέσω HTTPS:

git clone https://github.com/d-sfounis/bluechain_SSI.git

B2. Mέσω SSH:

git clone git@github.com:d-sfounis/bluechain_SSI.git

Και στις δύο περιπτώσεις, θα χαρούμε να λάβουμε σχόλια, ερωτήσεις ή προτάσεις αλλαγής επί του κώδικα. Είστε ευπρόσδεκτοι να υποβάλλετε pull requests από τα τοπικά σας repositories, στο main branch του Bluechain SSI :-)

NodeJS (v10.x++)

npm (v7.x++)

Solidity compiler (v0.8.x++)

Truffle Development Environment

Ganache for Ethereum

Το έργο μας, σε όλες τις εκδόσεις του και ακόμα κι όσο αναπτύσσεται, διατηρείται στο εξής εναποθετήριο (repository): Είναι λογισμικό Ανοικτού Κώδικα (Open Source), συμμορφώνεται στα πρότυπα της Ανοικτά Διαθέσιμης Επιστήμης (Open Science) κατά ΕΕ, και παραμένει και διαθέσιμο προς κάθε Πολίτη.

https://nodejs.org/en/
https://www.npmjs.com/get-npm
https://docs.soliditylang.org/en/v0.8.4/installing-solidity.html
https://www.trufflesuite.com/truffle
https://github.com/trufflesuite/truffle
https://www.trufflesuite.com/ganache
https://github.com/trufflesuite/ganache
https://github.com/d-sfounis/bluechain_SSI

Welcome to Bluechain Self Sovereign Identity (Bluechain SSI) — Technical documentation

This is a research project for blockchain-based Self Sovereign Identity, intended for eHealth Systems in Europe. This tech report is a project deliverable, written in Greek. ENG version shall follow.

Εισαγωγή

Το έργο "Bluechain Self Sovereign Identity for e-Health - Σύστημα Αυτοκυβερνούμενης Ταυτοποίησης βασισμένο σε τεχνολογίες blockchain" είναι ερευνητικό project καινοτόμας τεχνογνωσίας, αναμεταξύ του Αριστοτελείου Πανεπιστημίου Θεσσαλονίκης (τμ. Πληροφορικής) και της Γνώμων Πληροφορικής ΑΕ, και συγχρηματοδοτείται από το πρόγραμμα "ΚΟΥΠΟΝΙΑ ΚΑΙΝΟΤΟΜΙΑΣ 2021" του ΚΕΠΑ ΑΝΕΜ (ΕΣΠΑ Κεντρικής Μακεδονίας).

Το παρόν τεχνικό εγχειρίδιο χρήσης αφορά τον κώδικα που αναπτύχθηκε κατά τα παραδοτέα 2 και 3 του έργου, και αποσκοπεί στην τεκμηρίωση και τη διευκόλυνση χρήσης του αναπτυγμένου λογισμικού από τους χρήστες του.

Περιεχόμενα

🛠️ Λειτουργία του Bluechain SSI

Βήμα 1: Έναρξη Ganache

Το Bluechain SSI χρειάζεται να συνδεθεί σε ένα "ζωντανό" Ethereum blockchain network, αφού πάνω σε αυτό λειτουργεί.

Συστήνουμε: 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

Το ξεκίνημα του Ganache πριν από οποιαδήποτε χρήση του Bluechain SSI είναι απαραίτητο, εκτός αν διαδράτε με το σύστημα τελείως εξωτερικά, μέσω του ίδιου του Ethereum blockchain και των RPC APIs του. Μπορείτε επίσης να ρυθμίσετε το truffle να κοιτάει απευθείας πάνω στο blockchain, μέσω του truffle-config.js που περιλαμβάνεται στο πακέτο του κώδικα!

Δημιουργήστε ένα νέο blockchain workspace τύπου Ethereum μέσω του δεξιού βέλους (το Bluechain SSI δεν είναι ακόμη συμβατό με R3 Corda, κατά την τωρινή του υλοποίηση (v0.2):

2. Έναρξη και χρήση Bluechain SSI μέσω Truffle

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:

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


Δώστε ιδιαίτερη σημασία στο logging του Truffle, ιδιαίτερα στο δικό μας custom πεδίο: PassportManager contract deployed at address: ... — είναι η διεύθυνση στην οποία κατοικεί το Smart Contract μας, και μέσω της οποίας θα διαδρούμε με το σύστημα του Bluechain SSI.

Παρατηρήστε επίσης ότι ένα μικροποσό Ethereum αφαιρέθηκε από ένα από τα πορτοφόλια-διευθύνσεις σας (wallet addresses) στο Ganache. Αυτό αποτυπώνει το deployment cost του συστήματος, και παραμένει ακριβώς το ίδιο και στα live Ethereum chains.

⛑️ Ατομικές δοκιμές (unit tests) & επιδόσεις (performance)

Ατομικές δοκιμές (Unit tests)

Η σουίτα λογισμικού του Bluechain SSI συνοδεύεται από μια πλειάδα unit tests για να επιτυγχάνεται η διαρκής ορθότητα του κώδικα. Σκοπός των unit tests είναι οι βασικές λειτουργίες του συστήματος να συνεχίζουν να λειτουργούν ορθά και με τον ίδιο τρόπο, καθ'όσο αυτό αναπτύσσεται και επεκτείνεται πέραν της έκδοσης 0.3.0 (όπου βρισκόμαστε τώρα).

Στις τελευταίες εκδόσεις της Truffle, πλέον προτείνεται η δομή async/await έναντι των Promises. Είναι στα σύντομα μελλοντικά μας πλάνα να μεταπηδήσουμε τα unit tests σε αυτή τη μορφή, μιας και προσφέρουν περισσότερο έλεγχο επί της αλληλουχίας και της σειριακότητας των συναλλαγών στο σύστημα.

tests/test_initPassport.js
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);
  });
  */
  
});

Εκτέλεση της σουίτας δοκιμών (test suite)

Τρέξτε όλα τα παρόντα tests πολύ απλά, με την εντολή:

$ truffle test

Για ευκολία, προσφέρουμε άλλη μια εντολή για testing με maximum logging verbosity. Χρησιμοποιήστε την σε καταστάσεις που απαιτείται ενδελεχής έλεγχος του συστήματος: $ truffle test_everything

Και αναμένουμε ως output τα αποτελέσματα των validity & performance tests:

$ truffle test_everything
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 (κατά τις πιο "βαριές" λειτουργίες δημιουργίας νέας Ταυτότητας Χρήστη ή εισαγωγής νέων εγγράφων πιστοποίησης).

Αναλυτική λίστα μεθόδων & μεταβλητών (Reference)

Μία-προς-μία όλες οι μεταβλητές και λειτουργίες του συστήματος Bluechain SSI, και τεκμηρίωση.

Λίστα δομών και μεθόδων:

1. struct Passport

Αποτελεί βασική δομή αποτύπωσης ενός Διαβατηρίου, γεμάτο με έγγραφα Ταυτοποίησης, και που ανήκει/διαχειρίζεται από έναν Χρήστη. Περιέχει τις εξής μεταβλητές:

  • string flair_name: Ένα βοηθητικό ψευδώνυμο για το εκάστοτε Passport object. Δεν είναι απαραίτητο να συμπληρωθεί. Εν αρχή, λαμβάνει ψευδώνυμο ίδιο με το όνομα του Χρήστη.

  • address controller: Ο διαχειριστής του Διαβατηρίου, που έχει δικαιώματα αλλαγών πάνω του. Αποτυπώνεται ως blockchain address (διεύθυνση του Χρήστη). Εν αρχή, θέτεται να δείχνει στον αρχικό δημιουργό του Διαβατηρίου. Αν και αυτό συνήθως παραμένει έτσι κατά τη λειτουργία του 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 ενός εγγράφου.

  • mapping(bytes32 => bytes32) identity_files_LUT: Βοηθητικό hashmap array που δρα ως Lookup Table (LUT) stην προηγούμενη δομή identity_files. Τα hashmaps δεν επιτρέπουν αναζήτηση/iteration των keys τους, έτσι η λύση είναι να χρησιμοποιούμε δύο δομές δεδομένων, πλάι-πλάι: ένα hashmap και ένα hashmap LUT για την αναζήτηση των "γεμισμένων" ζευγαριών του πρώτου.

  • Οι επιπρόσθετες δομές για γρήγορη Ο(1) αναζήτηση, καθώς και οι λίστες για delegates (εκπροσώπους) παραμένουν αδρανείς προς το παρόν στην έκδοση 0.3.0, για οικονομία πόρων κατά τη χρήση του συστήματος.

2. mapping user_passports & mapping user_passports_LUT

H κύρια βάση δεδομένων Διαβατηρίων & Ταυτοτήτων των Χρηστών στο σύστημά μας. Σε αυτή τη δομή δεδομένων έγγειται η "καρδία" του έξυπνου συμβολαίου (smart contract) του Bluechain SSI. Αποθηκεύει και αντιστοιχίζει διευθύνσεις/πορτοφόλια Χρηστών σε ενεργά Διαβατήρια, που περιέχουν έγγραφα Ταυτοποίησης και Ισχυρισμών (Claims).

Τύπος: mapping (hashmap) ζευγαριών μορφής <address → struct Passport>

3. event PassportInitialized, AddedIDFileToPassport, Voted

Τα events (συμβάντα) είναι διευκολύνσεις ώστε να αφήνουν ιστορικό (logs) των πράξεών τους τα smart contracts, προς το "γενικό", non-blockchain internet. Τα πυροδοτούμε και τα στέλνουμε σε κατάλληλα σημεία στον κώδικα του συστήματος του Bluechain SSI, συνήθως ακριβώς όταν έχει ολοκληρωθεί μια λειτουργία, ώστε να σημάνουμε την επιτυχή της εκτέλεση. Έτσι, τα events που χτίζουμε και εκπέμπουμε, ανά τον κώδικα, είναι τρία (3):

  • event PassportInitialized, όταν αρχικοποιείται επιτυχώς ένα νέο Διαβατήριο, έπειτα από request νέου Χρήστη. Μαζί, εκπέμπουμε το αναγνωριστικό του νέου Διαβατηρίου και ποιος (ποια διεύθυνση) το ζήτησε.

  • event AddedIDFileToPassport, όταν προστίθεται επιτυχώς ένα Έγγραφο Ταυτοποίησης σε υπάρχον Διαβατήριο κάποιου Χρήστη. Μαζί, εκπέμπουμε το αναγνωριστικό του Διαβατηρίου όπου μόλις προστέθηκε το Έγγραφο, και το ίδιο το fingerprint του Εγγράφου.

  • event Voted, όταν ένας Χρήστης ψηφίζει για την αύξηση του σκορ εμπιστοσύνης (trust score) ενός Εγγράφου σε Διαβατήριο κάποιου άλλου Χρήστη. Μαζί, εκπέμπουμε το αναγνωριστικό του Διαβατηρίου στο οποίο μόλις επιδράσαμε, και ποιος (ποια διεύθυνση) έριξε την ψήφο εμπιστοσύνης.

Τύπος: event, που πυροδοτείται μέσα από τον κώδικα του έξυπνου συμβολαίου και μπορεί να ανιχνευθεί από οποιοδήποτε online πρόγραμμα που κοιτάει προς το blockchain του Bluechain SSI.

4. function hasInitializedPassport

Λειτουργία: Βοηθητική, ελέγχει αν ένας Χρήστης έχει όντως ένα ενεργό, αρχικοποιημένο Διαβατήριο.

Παράμετροι: address addr, η διεύθυνση/πορτοφόλι του Χρήστη τον οποίο ελέγχουμε.

Επιστρεπτέες τιμές: bool true ή false ανάλογα με το αν ο Χρήστης έχει ενεργό Διαβατήριο.

Τύπος: Συνάρτηση τύπου view, που σημαίνει ότι δεν απαιτεί αλλαγή του εσωτερικού αποθηκευτικού χώρου του smart contract, κι έτσι μπορεί να εκτελεστεί δωρεάν (zero gas fee).

5. function initPassport

Λειτουργία: Εκκινεί και αρχικοποιεί ένα ενεργό, κενό Διαβατήριο για έναν Χρήστη.

Παράμετροι: string memory nickname, ένα βοηθητικό ψευδώνυμο/identifier του Διαβατηρίου, για εύκολη αναζήτση και αναγνώριση από τους Χρήστες.

Επιστρεπτέες τιμές: Πλειάδα 2-διαστάσεων, tuple (string, address) όπου περιέχει το ψευδώνυμο του Διαβατηρίου που μόλις χρησιμοποιήθηκε, καθώς και την διεύθυνση/πορτοφόλι του Χρήστη που το δημιούργησε.

Τύπος: Κανονική συνάρτηση. Προκαλεί αλλαγές στον αποθηκευτικό χώρο του smart contract, κι έτσι η εκτέλεσή της φέρει κόστος (gas fee = gas_price * gas_limit).

Σημείωση: Κατά την initPassport(), ο αρχικός διαχειριστής (controller) του Διαβατηρίου θέτεται ως αυτός που το δημιούργησε. Αυτή η παραδοχή δεν είναι απαραίτητα αληθής κατά τη συνέχεια της λειτουργίας του συστήματος, αφού δίνεται η δυνατότητα σε έναν Χρήστη να αναθέσει τον έλεγχο του Διαβατηρίου του σε άλλους.

6. function addIDFileToPassport

Λειτουργία: Προσθέτει ένα νέο Έγγραφο Ταυτοποίησης σε ένα ενεργό Διαβατήριο του Χρήστη.

Παράμετροι:

  • address passport_id, η διεύθυνση/αναγνωριστικό του Διαβατηρίου στο οποίο θα προσθέσουμε το νέο Έγγραφο Ταυτοποίησης

Επιστρεπτέες τιμές: Πλειάδα 3-διαστάσεων, tuple (address, bytes32, uint) όπου η 1η συνιστώσα (address) είναι η διεύθυνση του Χρήστη και Διαχειριστή του Διαβατηρίου που μόλις πρόσθεσε το Έγγραφο, η 2η (bytes32) είναι το fingerprint του Εγγράφου που μόλις προστέθηκε, και η 3η (uint) είναι το trust score του νεοεισηχθέντος Εγγράφου — θα πρέπει να είναι 1!

Τύπος: Κανονική συνάρτηση. Προκαλεί αλλαγές στον αποθηκευτικό χώρο του smart contract, κι έτσι η εκτέλεσή της φέρει κόστος (gas fee = gas_price * gas_limit).

7. function voteForDocInPassport

Λειτουργία: Εντοπίζει ένα συγκεκριμένο Έγγραφο Ταυτοποίησης στο δοθέν αναγνωριστικό Διαβατηρίου, και αυξάνει το σκορ εμπιστόσυνης (trust score) του Εγγράφου κατά +1. Εκτελείται απαραιτήτως από Χρήστη διαφορετικό του ιδιοκτήτη του Διαβατηρίου.

Παράμετροι:

  • address passport_id, η διεύθυνση/αναγνωριστικό του Διαβατηρίου στο οποίο θα ψάξουμε για το Έγγραφο Ταυτοποίησης.

Επιστρεπτέες τιμές: Πλειάδα 3-διαστάσεων, tuple (address, bytes32, uint) όπου η 1η συνιστώσα (address) είναι η διεύθυνση του Χρήστη που μόλις ψήφισε μέσω της συνάρτησης, η 2η (bytes32) είναι το fingerprint του Εγγράφου που μόλις ψηφίστηκε θετικά, και η 3η (uint) είναι το trust score του Εγγράφου πλέον — θα πρέπει να είναι άνω του 1!

Τύπος: Κανονική συνάρτηση. Προκαλεί αλλαγές στον αποθηκευτικό χώρο του smart contract, κι έτσι η εκτέλεσή της φέρει κόστος (gas fee = gas_price * gas_limit).

8. function PassportExists

Λειτουργία: Βοηθητική, ελέγχει αν ένα Διαβατήριο υπάρχει (είναι ενεργό, έχει αρχικοποιηθεί) ή όχι.

Παράμετροι: address passport_id, η διεύθυνση/αναγνωριστικό του Διαβατηρίου.

Επιστρεπτέες τιμές: bool true ή false ανάλογα με το αν το Διαβατήριο υπάρχει.

Τύπος: Συνάρτηση τύπου view, που σημαίνει ότι δεν απαιτεί αλλαγή του εσωτερικού αποθηκευτικού χώρου του smart contract, κι έτσι μπορεί να εκτελεστεί δωρεάν (zero gas fee).

9. function DocExistsInPassport

Λειτουργία: Βοηθητική, ελέγχει αν ένα Έγγραφο Ταυτοποίησης υπάρχει σε ένα ενεργό Διαβατήριο κάποιου Χρήστη.

Παράμετροι:

  1. Passport storage p, η δομή Passport, έτοιμα δοσμένη, μέσα στην οποία θα αναζητήσουμε το Έγγραφο.

Επιστρεπτέες τιμές: bool true ή false ανάλογα με το αν το Έγγραφο υπάρχει μέσα στο δοθέν Διαβατήριο.

Τύπος: Συνάρτηση τύπου view, που σημαίνει ότι δεν απαιτεί αλλαγή του εσωτερικού αποθηκευτικού χώρου του smart contract, κι έτσι μπορεί να εκτελεστεί δωρεάν (zero gas fee).

Ομάδα Aνάπτυξης

Υπεύθυνοι για την ανάπτυξη του λογισμικού και του κώδικα στο παρόν έργο.

Παράρτημα: ο κώδικας των Έξυπνων Συμβολαίων του Bluechain SSI

Περιεχόμενα:

1. Smart Contract: PassportManager

contracts/PassportManager.sol
/* 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 */
}

2. Smart Contract: Migrations

To Migrations contract και γενικά η λογική των "migrations over redeployments" αποτελεί εσωτερική σχεδιαστική επιλογή του ίδιου του Truffle. Στην ουσία, είναι τρόπος του Truffle να "βοηθάει" το σύστημά μας με το να κρατά ιστορικό των εκδόσεων του Bluechain SSI που έχουν ενεργοποιηθεί πάνω στο blockchain, και να αποφεύγει να ξανα-ενεργοποιεί κάτι όμοιο που ήδη έχει ξαναενεργοποιηθεί στο παρελθόν (σώζοντάς μας, έτσι, deployment costs).

Βρίσκουμε ελάχιστη χρησιμότητα σε αυτήν την αρχιτεκτονική. Μάλιστα, η Ομάδα μας αντιπαραθέτει τα πλεονεκτήματα που προσφέρει η χρήση Migrations με τα μειονεκτήματα του structure & debugging clutter που δημιουργούνται στο σύστημα μας, εξ'αιτίας αυτής. Πολλές φορές κατά την υλοποίηση του συστήματος χρειάστηκε να δαπανήσουμε περισσότερο χρόνο πάνω από τα logs μας, για να καταλάβουμε τί είναι irrelevant Migrations functionality και τι πραγματικά αφορούσε το PassportManager contract μας.

Για πληρότητα, ωστόσο, παραθέτουμε το smart contract και εδώ. Το συμβόλαιο είναι άρρηκτα συνδεδεμένο με τα migrations .js files στο directory ./migrations, όπου κρατείται στην ουσία το ιστορικό των μεταβολών και των ενεργοποιήσων των έξυπνων συμβολαίων του συστήματός μας.

contracts/Migrations.sol
// 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;
  }
}

Υπενθυμίζουμε ότι όλα τα παραδοτέα 1, 2, 3 του έργου καθώς και οι συνοδευτικές μελέτες και τεχνικές εκθέσεις, διατίθενται ανοικτά και ελεύθερα ως Ελεύθερο Λογισμικό (Free Open Source Software) κάτω από άδεια χρήσης .

🏗️

📕

💻

👓

Η πιο εύκολη λύση για αυτό είναι ένα local Ethereum network μέσω Ganache (το εγκαταστήσαμε στο προηγούμενο βήμα - ). Το Ganache μπορεί να ρυθμιστεί ώστε να κοιτάει σε local ETH network (φτιαγμένο ως ειδικό instance αποκλειστικά και μόνο στο μηχάνημά σας), ή σε live ETH networks (Ethereum Mainnet, Testnets Ropsten, Koeva, Rinkeby, Goerli) ή ακόμα και το Binance Smart Network (αφού αυτό είναι 1:1 clone του Ethereum).

Συμπεριλαμβάνουμε 4 unit tests (μορφής it()) σε μορφή Javascript Promise. Καθένα από αυτά, έχει φτιαχτεί για να ελέγχει ενδελεχώς μία εκ των 4 βασικών λειτουργιών του Bluechain (βλέπε επόμενη ενότητα - )

Υπενθυμίζουμε ότι η υλοποίηση της δομής αποθήκευσης εγγράφων ως hashmap με associated trust scores ακολουθεί την προτεινόμενη δομή του CALYPSO paper των Eleftherios Kokoris-Kogias et al (2020) όπως αυτό μελετήθηκε, μεταξύ άλλων προσεγγίσεων, στην πρώτη Μελέτη Χαρτογράφησης της ομάδας μας κατά το 1ο παραδοτέο του έργου. Η μελέτη μας είναι διαθέσιμη εδώ:

bytes32 id_file, το fingerprint του Εγγράφου. Η συνάρτηση δέχεται οποιοδήποτε document digest/fingerprint των 32 byte (256 bit) — η μέθοδος που χρησιμοποίησε η Ομάδα μας και προτείνουμε, για την παραγωγή ισχυρών document fingerprints προς εισαγωγή στο Bluechain SSI, είναι ο σε spec SHA3-256 .

bytes32 doc_id, το fingerprint του Εγγράφου που θα αναζητήσουμε στο προηγούμενο Διαβατήριο και του οποίου το trust score θα αυξήσουμε με +1, διά "ψήφου εμπιστοσύνης". Υπακούει στο ίδιο cryptography standard (KECCAK256) όπως και η .

bytes32 doc_id, το fingerprint του Εγγράφου που θα αναζητήσουμε μέσα στο Διαβατήριο p. Ξανά, είναι της ίδιας μορφής (KECCAK, 256 bits) όπως και στην .

1. Σφούνης Δημήτρης ( )

2. Σταμέλος Ιωάννης ( )

Υπενθυμίζουμε ότι οι τελευταίες εκδόσεις όλων των Έξυπνων Συμβολαίων του Bluechain SSI μπορούν πάντα να βρεθούν (και να σχολιαστούν!) στο δημόσιο GitHub directory μας: 🐈 Όλα τα τελευταία νέα του project: Bluechain Self Sovereign Identity διατηρούνται στη σελίδα μας: 🌊

To PassportManager contract είναι το κύριο Έξυπνο Συμβόλαιο του συστήματος BluechainSSI. Ενσωματώνει όλες τις κύριες και βοηθητικές λειτουργίες που περιγράφησαν κατά το Reference της προηγούμενης ενότητας (). Συνάμα, περιέχει εκτενή σχολιασμό μέσα στον ίδιο τον κώδικα ώστε να ενισχύεται η ανοικτότητα και η κατανοητικότητά του προς τον προγραμματιστή-αναγνώστη.

GNU GPL v3.0
Εγκατάσταση & Χρήση του Bluechain SSI
🛠️ Λειτουργία του Bluechain SSI
⛑️ Ατομικές δοκιμές (unit tests) & επιδόσεις (performance)
Αναλυτική λίστα μεταβλητών και μεθόδων (Reference)
Παράρτημα: ο κώδικας των Έξυπνων Συμβολαίων του Bluechain SSI
Ομάδα Ανάπτυξης έργου — Επικοινωνία
📕 Αναλυτική λίστα μεθόδων & μεταβλητών (Reference)
https://blockchainehealth.page.link/study
KECCAK algorithm
📭
dsfounis@bluechain.tech
https://bluechain.tech#our-team
https://dsfounis.com
📭
stamelos@csd.auth.gr
https://www.csd.auth.gr/member/stamelos-ioannis/
https://stamelos.webpages.auth.gr/
https://github.com/d-sfounis/bluechain_SSI
https://bluechain.tech/
📕 Αναλυτική λίστα μεθόδων & μεταβλητών (Reference)
Εγκατάσταση και Xρήση του Bluechain SSI
Ολόκληρος ο κώδικας του PassportManager.sol smart contract
Ολόκληρος ο κώδικας του Migrations.sol smart contract
struct Passport
mapping user_passports & mapping user_passports_LUT
event PassportInitialized, AddedIDFileToPassport, Voted
function hasInitializedPassport
function initPassport
function addIDFileToPassport
function voteForDocInPassport
function PassportExists
function DocExistsInPassport
function addIDFileToPassport
function addIDFileToPassport
Το περιβάλλον του Ganache