Node Module TyDIDs - Grundlagen

Das NPM Modul von TyDIDs ist eine kleine Erweiterung von Ethers JS, welche die Nutzung innerhalb eines Trustframeworks vereinfacht. Diese Anleitung gibt eine Einführung in die Verwendung, wobei sich an einem Anwendungsfall der STROMDAO aus dem Projekt ID-Ideal für den Bereich der Energiewirtschaft orientiert wird. Diese Dokumentation vereinfacht an einigen Stellen zur besseren Verständlichkeit, soweit dies keinen Unterschied für gezeigte Funktion macht.

Anwendungsfall:

Treibhausgasminderung für die Ladung eines E-Auto.

Nach dem Abschluss eines Ladevorgangs möchte Alice (Ladende) ein Nachweis über die eingesparte Treibhausgasemissionen von Bob (Energie Service Anbieter) erhalten, welches sie in ihrer digitalen Brieftasche sammeln kann. 

Anforderungen an die Umsetzung

Ablauf der Kommunikation

TyDIDS-Grundlagen-Ladevorgang.drawio (1).png

  1. Sobald Alice den Ladevorgang beendet hat, fordert sie mit der Kennung des Ladevorgangs einen Beleg von Bob an.
  2. Bob ermittelt alle Daten, die zum Ladevorgang gehören (besonders die eingesparte Treibhausgasemission)
  3. Bob übermittelt den vollständigen Datensatz an Alice 
  4. Alice und Bob berechnen unabhängig einen Hash für den Datensatz
  5. Alice fordert eine Signatur von Bob für den Ladevorgang an
  6. Bob schreibt den Hash des Datensatzes mit dem Vermerk, dass dieser gültig ist in die Blockchain, wodurch er einen Transaktionshash erhält
  7. Bob übermittelt an Alice den Transaktionshash, sowie eventuell benötigte Untermengen der bestätigen Werte digital signiert an Alice. 
  8. Alice legt die von Bob erhalten Daten (Zertifikate/Signatur) in ihrer digitalen Brieftasche ab.

Die Schritte 1-3 gehören zum Aufgabenblock des Certificate Sign Requests. 

Die Schritte 4-5 sind Zwischenschritte zur Sicherstellung einer verteilten Resilienz gegenüber Implementierungsunterschiede.

Die Schritte 7-8 gehören zum Aufgabenblock des Self Sovereign Identity Konzeptes.

Umsetzung in TyDIDs

Das Repository von Github clonen und installieren oder GitPOD verwenden. 

Die gesamte Kommunikation zwischen Alice und Bob wird in der Datei index.js implementiert werden, die zunächst wie folgt aussieht:

const tydids = require("tydids");

const app = async function() {
 
    // Your Code HERE
    
}
app();

Die Zeile 5 wird gelöscht und zunächst einige Initialisierungen vorgenommen.

 const walletAlice = tydids.wallet(); // Wallet mit einem zufälligen privaten Schlüssel
 const walletBob = tydids.wallet(); // Wallet mit einem zufälligen privaten Schlüssel
 const msgEndOfCharging = { txid: "1337", owner:walletAlice.address } // Nachricht am Ende des Ladevorgangs 

Schritt 1

Alice sendet zunächst eine Nachricht an Bob mit dem Inhalt { txid: "1337" } wodurch sie anzeigt, dass der Ladevorgang mit der Kennung 1337 beendet wurde und sie hierfür einen Beleg benötigt.

Schritt 2

const receiptPayload = {
        txid: msgEndOfCharging.txid, // Ladevorgangskennung für den Beleg
        owner:msgEndOfCharging.owner, // Eigentümer des Beleges
        ghgSaving: 355,   // Angabe über die eingesparte Treibhausgasemission
        signee:walletBob.address  // Bob macht sich kenntlich als der zukünftige Aussteller 
    }

Bob ermittelt, dass die Einsparung für diesen Ladevorgang bei 355 Gramm gelegen hat.

Schritt 3

Bob übermittelt den gesamten Inhalt von receiptPayload an Alice. Zur einfacheren Nachvollziehbarkeit lassen wir diesen Schritt auf der Konsole ausgeben.

 console.log("Schritt 3",receiptPayload);

 Die Ausgabe könnte wie folgt aussehen:

Schritt 3 {txid: '1337', ghgSaving: 355, signee: '0x2142141126242987D0b3FA77223434389AA99Bf3'}

Da bei jedem Aufruf ein neuer privater Schlüssel für die beiden Wallets erzeugt wird, unterscheidet sich die Angabe unter "Signee" jeweils.

Schritt 4

Sowohl Alice als auch Bob berechnen nun unabhängig voneinander einen Hash aus der Nachricht, die in Schritt 3 übermittelt wurde.

    const hashAlice = walletAlice.tydids.hashMessage(receiptPayload);
    const hashBob = walletBob.tydids.hashMessage(receiptPayload);
    console.log("Schritt 4",hashAlice,hashBob);

Die beiden Hashes müssen übereinstimmen. Dieser Schritt ist wichtig, um zu verhindern, dass bei der Verwendung von unterschiedlichen Versionen von Bibliotheken und Implementierungen tatsächlich ein Konsens zwischen Alice und Bob besteht. 

Schritt 5

Da Alice sich noch nicht ganz sicher ist, was sie später benötigt, fordert sich von Bob jetzt unterschiedliche unterschriebene Belege an.

  const requestPureExistence = {
        hash:hashAlice
    } // Reiner Existenznachweis

  const requestExistenceAndOwnership = {
    hash:hashAlice
    owner:walletAlice.address
  } // Existenz und (initiale) Eigentümerschaft

  const requestFullCertificate = {
    hash:hashAlice
    owner:walletAlice.address,
    ghgSaving:355
  } // Existenz, Eigentümerschaft und Einsparung

Schritt 6 und 7

Bei der ersten Anfrage von Alice unterzeichnet Bob den Hash lediglich und vermerkt mit einer Transaktion dies in der Blockchain (hier nicht gezeigt). 

    let signedPureExistence = await walletBob.tydids.signMessage(requestPureExistence);
    console.log("Signatur Reiner Existenznachweis",signedPureExistence);
    let hashSignedPureExistence = await walletBob.tydids.hashMessage(signedPureExistence);
    console.log("Signatur Hash",hashSignedPureExistence);

Analog hierzu erstellt Bob auch die anderen beiden Unterschriften und dazugehörigen Hashes:

    let signedExistenceAndOwnership = await walletBob.tydids.signMessage(requestExistenceAndOwnership);
    let hashExistenceAndOwnership = await walletBob.tydids.hashMessage(signedExistenceAndOwnership);

    let signedFullCertificate = await walletBob.tydids.signMessage(requestFullCertificate);
    let hashFullCertificate = await walletBob.tydids.hashMessage(signedFullCertificate);

    let signedTranferToken = await walletBob.tydids.signMessage(requestTranferToken);
    let hashTransferToken = await walletBob.tydids.hashMessage(signedTranferToken);

Am Ende hat Alice noch eine weitere Unterschrift eingefordert für einen Transfertoken. Dieser kann nachgelagert verwendet werden, um die Eigentümerschaft des Beleges an einen Dritten zu übertragen. 

Schritt 8

Alice speichert als Beleg für die Treibhausgasminderung aus diesem einen Ladevorgang, alle Unterschriften von Bob sowie die dazugehörigen Hashes.

Vollständiger Beispielcode

const tydids = require("tydids");

const app = async function() {
 
    const walletAlice = tydids.wallet(); // Wallet mit einem zufälligen privaten Schlüssel
    const walletBob = tydids.wallet(); // Wallet mit einem zufälligen privaten Schlüssel
    const msgEndOfCharging = { txid: "1337",owner:walletAlice.address } // Nachricht am Ende des Ladevorgangs 

    const receiptPayload = {
        txid: msgEndOfCharging.txid, // Ladevorgangskennung für den Beleg
        owner: msgEndOfCharging.owner, // Eigentümer des Beleges
        ghgSaving: 355,   // Angabe über die eingesparte Treibhausgasemission
        signee:walletBob.address  // Bob macht sich kenntlich als der zukünftige Aussteller 
    }

    console.log("Schritt 3",receiptPayload);

    const hashAlice = walletAlice.tydids.hashMessage(receiptPayload);
    const hashBob = walletBob.tydids.hashMessage(receiptPayload);
    console.log("Schritt 4",hashAlice,hashBob);
    
    const requestPureExistence = {
        hash:hashAlice
    } // Reiner Existenznachweis

    const requestExistenceAndOwnership = {
        hash:hashAlice,
        owner:walletAlice.address
    } // Existenz und (initiale) Eigentümerschaft

    const requestFullCertificate = {
        hash:hashAlice,
        owner:walletAlice.address,
        ghgSaving:355
    } // Existenz, Eigentümerschaft und Einsparung

    const requestTranferToken = {
        hash:hashAlice,
        transferToken: Math.random()
    } // Beleg an einen anderen Eigentümer übertragen

    let signedPureExistence = await walletBob.tydids.signMessage(requestPureExistence);
    console.log("Signatur Reiner Existenznachweis",signedPureExistence);
    let hashSignedPureExistence = await walletBob.tydids.hashMessage(signedPureExistence);
    console.log("Signatur Hash",hashSignedPureExistence);

    let signedExistenceAndOwnership = await walletBob.tydids.signMessage(requestExistenceAndOwnership);
    let hashExistenceAndOwnership = await walletBob.tydids.hashMessage(signedExistenceAndOwnership);

    let signedFullCertificate = await walletBob.tydids.signMessage(requestFullCertificate);
    let hashFullCertificate = await walletBob.tydids.hashMessage(signedFullCertificate);

    let signedTranferToken = await walletBob.tydids.signMessage(requestTranferToken);
    let hashTransferToken = await walletBob.tydids.hashMessage(signedTranferToken);

}
app();

Ergebnis

Auf Basis der Unterschriften, die Alice von Bob erhalten hat, kann sie nun gegenüber Dritten unter voller Achtung der Privatsphäre einen Nachweis führen (Verifiable Presentation), wobei der Dritte lediglich Bobs Unterschriften vertrauen muss, nicht aber mit diesem erneut in Kontakt treten muss. Sollte Alice an den Dritten den Beleg weitergeben (Transfer der Eigentümerschaft), so wird sie den Transfertoken selbst unterschreiben und diese Unterschrift an den Dritten gemeinsam mit dem Transfertoken weitergeben. Dieser kann dann von Bob eine Vernichtung des Zertifikates von Alice anfordern und eine Neuausstellung an ihn.

Im Rahmen dieser grundlegenden Einführung wurde nicht explizit auf das Thema Aggregation oder die Verhinderung von Doppeleinlösungen eingegangen. Die Kommunikation wurde nur schematisch gezeigt. Bei einer praktischen Implementierung muss der Empfänger einer Nachricht eine abgesicherte Information über den Sender der Nachricht haben. Hier kann zum Beispiel das in der Energiewirtschaft für die Marktkommunikation verwendete AS4 Protokoll zum Einsatz kommen.


Revision #4
Created 19 November 2022 00:40:19 by Thorsten Zoerner
Updated 19 November 2022 03:31:49 by Thorsten Zoerner