Btn_Burger

Nach dem Meltdown: Wie wir Event-Plattformen neu denken können

05.11.2019 – Stefan Adolf

Ein dezentralisierter Ansatz für die Verwaltung von Veranstaltungen, Konferenzen und Teilnehmern

Anfang Oktober erhielten viele Veranstalter lokaler Communitys eine Benachrichtigung vom wework-eigenen meetup.com, in der es hieß, dass sie planen, Änderungen an ihrem Geschäftsmodell zu evaluieren. Kurz gesagt: Ihre Idee war es, jedem Teilnehmer eine Gebühr von 2 Dollar pro RSVP zu berechnen und gleichzeitig die jährlichen Kosten für Organisatoren auf 24 Dollar zu senken. Was sich tatsächlich wie eine lohnende Idee anhört, um die Teilnehmerzahlen zu erhöhen, könnte leicht als verzweifelter Versuch interpretiert werden, die verbleibenden Vermögenswerte zu monetarisieren. Wenn Sie an der ganzen Geschichte interessiert sind, The Verge hat hier eine fantastische Zusammenfassung.

Dieser Vorfall, den ich persönlich als "Meetup Meltdown" bezeichne, macht eines sehr deutlich: Solange man sich auf eine dritte Partei verlässt, ist man voll und ganz auf deren guten Willen angewiesen. Sie legen die Regeln fest, nach denen wir alle spielen müssen und sie können sie jederzeit ändern. Noch schlimmer: Wenn sie deswegen untergehen, nehmen sie uns mit.


Im Anschluss an die Ankündigung haben viele Menschen Meetup.com sofort verlassen. Ungeachtet der nicht allzu schlimmen Idee, eine lächerlich niedrige Gebühr zu verlangen (und sogar zurückzugeben!), wollte niemand derjenige sein, der die Rechnung für WeWorks katastrophalen Versuch, an die Börse zu gehen, bezahlt. Was eine legitime Entscheidung ist. Aber genau hier liegt das Problem: Wenn man nicht auf Meetup.com ist – wo baut man denn dann eine Community auf? Plötzlich sprossen die Alternativen nur so aus dem Boden, wie die Blumen nach dem Regenschauer in der Wüste. Es fühlte sich an, als würden Tausende Tweets von besorgten Meetup-Gastgebern unsere Timelines füllen und alle fragten: "Welche Alternative würdest du empfehlen?" Und davon gibt es nicht gerade wenige.

Denkt man nur einen Moment darüber nach, wird klar: Jeder Versuch, von Meetup.com auf eine andere Plattform zu wechseln, muss böse enden. Wenn diese andere Plattform erst einmal die gesamte Last der Meetup-Bewegungen des Planeten Erde bewältigen muss, wird sie früher oder später auch zu dem Schluss kommen, dass es aus dem einen oder anderen Grund eine gute Idee sein könnte, eine Gebühr zu verlangen. Es ist also Zeit für intelligentere Ansätze, die es uns erlauben, Events neu zu denken.

Der Gründer von FreeCodeCamp Quincy Larson hat nur Stunden nach dem Meltdown einen Tweet gepostet, der seine weltweit aktive Coder-Gemeinschaft ermutigt, sofort mit dem Aufbau einer dezentralen Alternative für FCC Chapter zu beginnen. Und die Community folgte.

Innerhalb von Stunden startete ein discord.gg/vbRUYWS mit etwa tausend mehr oder weniger erfahrenen Hackern, von denen viele scheinbar in Frontend-Angelegenheiten bewandert sind. Nur einen Tag später öffnete sich ihr Github-Repo und die Diskussion mündete in einem endlosen Strom von Feature Requests. Alle schienen eine gute Idee zu haben, wie die Organisation von Events für lokale Communities verbessert werden könnte. Jeder wollte mit an Bord sein, wenn die ganze Welt beginnt, eine Open-Source-Meetup-Community-Plattform aufzubauen. Das Erste, was auf der README zu sehen war github.com/freeCodeCamp/chapter/blob/master/data/schema.png, das die Beziehungen zwischen Nutzern, Organisatoren, Veranstaltungsorten und Events darstellt – und es war kein Zufall, dass es ähnlich aussah wie Konzepte, die wir bisher unter einem anderen Namen kannten: Meetup.com.

Das FreeCodeCamp "Chapter" Repo geht jedoch noch einen Schritt weiter. Sie haben erkannt, dass eine zentralisierte Plattform früher oder später mit ähnlichen Problemen enden wird wie unsere föderierten Freunde, also wollen sie die beweglichen Teile auf so viele Schultern wie möglich verteilen. Lokale Communities würden ihre eigenen Knotenpunkte betreiben und die containerisierte Chapter-Software auf ihnen installieren.

Der einzige zentralisierte Teil des Systems – zumindest zum Zeitpunkt dieses Artikels – wäre ein Entdeckungsdienst, der es neuen Benutzern ermöglicht, die Knotenpunkte ihrer lokalen Community zu finden. Eine hervorragende Idee. Aber man bräuchte immer noch Personen, die bereit sind, aktiv zu werden, einen laufenden Chapter-Knoten zu installieren und zu warten (was natürlich nicht kostenlos ist), und allen zu sagen, sie sollen sich darauf anmelden.

Diese Art von verteilten, verknüpften Netzwerken ist nicht ganz neu: Mastodon und Matrix (das Rückgrat von Riot.im) bauen ziemlich erfolgreich Twitter- und Telegramm-Ersatzdienste auf. Kazaa und Diaspora zeigten schon früh, dass P2P-kontrollierte Dateisysteme und soziale Netzwerke, die von global verteilten Peer-Knoten betrieben werden, die Perspektive hatten, die Schranken der Zentralisierung zu durchbrechen.

Aber wollen wir wirklich im Jahr 2020 Software mit relationalen Datenbanken und maschinenbasiertem Anwendungs-Hosting entwickeln? SQL stammt aus dem Jahr 1974, und in Zeiten von Serverless Computing hinterlässt der Betrieb eines Servers auch hier das flaue Gefühl, mit Zombie-Technologie zu arbeiten.

Die Anfänge der Dezentralisierung

Ein Gespenst streift durch die Web-Stadt und sein Name ist "Web3". Ein Begriff, der an die ältere Idee des "Web2.0" anknüpft, was zu einem Synonym für Anwendungen wurde, die Webtechnologien nutzen und Daten mit ihren Heimservern asynchron austauschen, auch bekannt als "AJAX". Bei Web3 geht es um dezentralisierte Anwendungen und Protokolle: Nicht ein einzelner Server, ein Unternehmen oder ein Entwickler sollte für die Betriebszeit, die Assets und die Daten verantwortlich sein, sondern das Netzwerk als Ganzes. Das Web3-Konzept hat bereits große Schritte in Richtung Produktionsbereitschaft unternommen, um die Art und Weise zu ändern, wie wir verteilten Code ausführen und den vertrauenswürdigen Anwendungsstatus zwischen Peers synchronisieren.

Eine der anerkanntesten und bewährtesten dezentralen Anwendungsplattformen ist die Ethereum Virtual Machine (EVM). Auch heute verstehen wahrscheinlich viele Menschen die auf einer Blockchain basierende Ethereum-Plattform falsch – als einen bloßen ICO-fähigen, Token-Mining, Crypto-Kiddie, ökologisch fragwürdigen Krypto-Betrug. Oder einfach gesagt, ein Bitcoin auf Steroiden. Nur, dass es das nicht ist.

Auf der Grundlage von Etherum läuft auf jedem Knoten ein Bytecode Interpreter. Miner führen den in die Ethereum Blockchain geschriebenen Code gleichzeitig und deterministisch aus, während sie neue Blöcke minen. Kunden können ausführbaren EVM-Bytecode zu ihren Transaktionen hinzufügen, um ihn von Miners ausführen zu lassen, und da er als Blockchain-Transaktionscode geschrieben und gehasht wurde, ist er unveränderbar, unausführbar und unzerstörbar. Daher bezeichnet man diese Art von Code als smart contract: nicht verhandelbare Regelwerke, die für immer in Stein gemeißelt sind.

Nur ein lustiger Exkurs: Der Begriff "intelligenter Vertrag" wurde in den 90er Jahren von dem Mathematiker Nick Szabo geprägt, lange bevor man an Blockchains gedacht hat. Einige Leute nehmen an, dass Szabo tatsächlich der Mann hinter dem ominösen "Satoshi Nakamato" ist, der einst mit einem Whitepaper den Anstoß für Bitcoin gab (was er seitdem immer bestritten hat). Und noch etwas lustiges: Ein Billionstel von 1 Ether wird als Hommage an seinen theoretischen Beitrag auch als 1 "Szabo" bezeichnet.

Verlässt man die philosophische Ebene, fühlt sich das Coden intelligenter Verträge nicht allzu anders als in zentralisierten Programmierkonzepten. Während jeder frei ist, eine eigene zu entwickeln, hat die Ethereum Stiftung bereits 2014 ihre Smart Contract Sprache "Solidity" eingeführt. Smart Contracts mit Solidity haben einen intrinsischen Zustand, haben öffentliche und private Methoden, um den Zustand des Contracts verändern zu können, können Ereignisse auslösen, können einen Wert enthalten (ausgedrückt als Ether Balance), unterstützen reine Funktionen und können Code von anderen Verträgen und Schnittstellen erben. Für die meisten Entwickler sollten sich Smart Contracts mit Solidity daher vertraut anfühlen: Sie sind mehr oder weniger Klassen. Oder präzise ausgedrückt: Klassen für persistente Objekte, die ihr Verhalten und ihr Zustandsschema repräsentieren. Viele Leute können potenziell denselben Smart Contract auf Ethereum einsetzen und damit Instanzen davon erzeugen.

Ein recht vollständiges Beispiel für einen fungiblen ERC-20-Vertrag in Solidity von Gilad Haimov

Die Interaktion mit einem Smart Contract in einer zustandsverändernden Weise ist nicht umsonst. Miners, die den Code der Contracts ausführen, müssen CPU-Zyklen ausführen, um den kompilierten Bytecode auszuführen. Ethereum führt daher das Konzept von "Gas" ein, um die Gebühren darzustellen, die für jeden Schritt der Code-Ausführung ausgegeben werden. Abhängig von der Komplexität der Operation schätzt ein Ethereum-Kunde eine Gasmenge, die für die Berechnung benötigt wird. Zum Zeitpunkt der Ausführung übersetzt ein Miner den Gaswert in "echte" Ether und erwartet, dass er zusammen mit der vertragsaufrufenden Transaktion gesendet wurde – wenn die gesendeten Mittel nicht ausreichen, lehnen die Miner die Transaktion ab. Jeder Nutzer, der mit einem Smart Contract in schreibend interagieren will, muss daher eine entsprechende Menge Gas mit dem Aufruf übergeben.

Smart Contracts sind nicht zu stoppen und ermöglichen die Erstellung von so genannten dezentralen Apps (oder kurz Đapps). Aus einer sehr vereinfachten praktischen Sicht sind es Frontend-Anwendungen, die das Ethereum-Ledger als Ereignisquelle und Datenbank verwenden.

Anfänge einer dezentralisierten Veranstaltungsplattform

Anfang August 2019, während einer internen Hacking-Veranstaltung namens "BREAKOUT", bildete ein Team von 5 Turbine Kreuzberg-Ingenieuren, POs und agilen Trainern das Team "Ethickets", um die Konzepte von Ethereum kennenzulernen und einen einfachen Meetup-Klon zu erstellen.

Unsere erste Idee war, nur eine Liste der RSVPed-Teilnehmer im Ledger zu speichern und alle Veranstaltungsdetails in einer Symfony basierten Backend-Anwendung zu pflegen, die mit Ethereum auf einem lokalen Geth-Knoten über eine Elixir-basierte Gateway-Anwendung interagiert. Ein React Native Client sollte es den Benutzern dann ermöglichen, "Tickets" für ein Ereignis zu entdecken und zu beanspruchen. An der Tür der Veranstaltung könnte jemand manuell prüfen, ob ein Teilnehmer eine öffentliche Rede halten kann, die im Smart-Vertrag gespeichert ist.

Wer etwas Erfahrung mit Web3 hat, wird schnell die vielen Mängel unseres allerersten Ansatzes bemerken: Die Symfony-Backend-Anwendung ist letztlich zentralisiert. Sie könnten den Elixir-Teil ganz überspringen und den Elixir-Backend-Interaktionscode (der für die Erstellung von Ereignissen benötigt wird) auf die PHP-Seite verschieben. Wir haben das nicht getan, weil unser Symfony-Entwickler sich dabei unwohl fühlte, aber es ist sicherlich möglich.

Ein weiterer kniffliger Teil unseres Ansatzes ist die Verwendung von React Native: Wie sich herausstellt, ist das Ausführen eines Web3-Clients, der mit Ethereum interagiert nicht die einfachste Sache, auf der nativen Plattform zu erreichen: Das liegt vor allem an den Knoten. Die Krypto-Bibliothek von js, die mit der Web-Plattform inkompatibel ist und die entsprechenden nativen Plugins kompiliert, führt uns in den Kaninchenbau von iOS-Pods und Android-JARs, um in der Abhängigkeitshölle zu verbrennen. Wer wirklich mit Etherum von einem Web/nativen Client aus interagieren muss, dem können wir das jetzt empfehlen ethers.js, aber wir haben das während der BREAKOUT-Woche nicht entdeckt.

Am Ende hatten wir einen funktionierenden Prototyp, der einen einfachen Smart Contract wie diesen ausgeführt hat:

pragma solidity >=0.4.22 <0.6.0;
contract ETHicketEvent {
address payable hoster;

mapping (address => uint8) tickets;

constructor() public {
hoster = msg.sender;
}

function requestTicket(address attendee) payable public {
require (msg.value > 1000000);
hoster.transfer(msg.value);
tickets[attendee] += 1;
}

function getTicketCount(address requester) public view returns (uint8) {
require(msg.sender == hoster);

return tickets[requester];
}
}

Um grob zu erklären, was hier vor sich geht: Jeder, der eine Veranstaltung ausrichten möchte, kann eine neue Instanz dieses Smart Contracts einsetzen und wird so zu seinem anerkannten Eigentümer (Hoster = msg.sender). Um ein Ticket zu "kaufen", rufen die Teilnehmer die Methode requestTicketmethod auf, die mindestens ein MWei mitschickt. Der Ticketpreis wird sofort an den Veranstalter überwiesen. Eine interne Hash-Tabelle (Mapping(Adresse => uint8) Tickets) wird bei jeder Transaktion inkrementiert, so dass ein Teilnehmer mehr als ein Ticket kaufen kann.

Die Idee war, dass das zentralisierte Symfony Backend den Überblick über Smart Contract Instanzen behalten würde, was es den Veranstaltern effektiv ermöglichen würde, alle Veranstaltungsdetails zentral zu verwalten und Ethereum nur für den Verkauf und die Pflege von "Tickets" zu nutzen. Die Eigentümer von Veranstaltungen würden nicht unbedingt Kenntnisse über die Blockchain in diesem Konzept benötigen. Wie man auch in einer ropsten.etherscan.io/address/0x575a50b88c368a78ec5ab775c05f6aa87b15bae6 sehen kann, geschah dies mehrmals während der Breakout-Woche.

Nachdem wir bei der Interaktion mit Web3 in React Native gescheitert waren, haben wir schließlich die brandneue React basierte Version von ionic auf dem Frontend verwendet und das bekannteste Desktop- und Mobilgerät (noch in der Beta-Phase) Ethereum Interaktionstool MetaMask alias "The Fox" eingesetzt. Beim Start lädt unsere für Mobiltelefone optimierte Webanwendung alle Ereignisse vom zentralen API-Endpunkt und erstellt eine neue Web3-Wallet und ein neues Web3-Konto, indem sie einen injizierten (in unserem Fall: fest eingebundenen) privaten Schlüssel verwendet. Von hier aus konnten wir Anfragen an die intelligenten Vertragsadressen stellen, die uns die API zur Verfügung gestellt hat, so dass wir effektiv in der Lage waren, Tickets von einem festen Benutzerkonto aus zu "kaufen". Wir haben das Ropsten-Testnet über einen intermediären Infura-Knoten JSON-RPC verwendet, aber wir haben es auch mit unserem eigenen geth-basierten Ropsten-Knoten getestet – was ebenso gut funktionierte. Leider erlaubte es unser Netzwerk nicht, es über Wifi zu verbinden, was es für Demonstrationszwecke unbrauchbar machte.

Our smart contract in the Remix IDE with active MetaMask plugin

Exkurs: Đappcon

Der letzte Augenöffner
Was wir auf jeden Fall gelernt haben: Entweder man geht dezentralisiert oder nicht. Jeder zentralisierte Teil im gesamten Systems würde jeden anderen zerstören. Damit im Sinne habe ich Đappcon im August 2019 besucht.



Eine dreitägige Veranstaltung, die sich vollständig auf die neuesten Entwicklungen in der Welt des Ethereum konzentrierte, und sie war keine Enttäuschung. Hunderte von internationalen Gästen kamen in meine Heimatstadt, und ich fühlte mich plötzlich sehr verstanden – ich merkte, dass wir uns nicht auf irgendeinem esoterischen Pfad auf einem mysteriösen Alien-Tech-Stack befanden, sondern dass wir nicht gut genug auf das hörten, was so viele der Teilnehmer von Đappcon bereits verstanden: das Web2.0, wie wir es kennen, ist tot, die Zukunft hat schon vor Jahren begonnen. Smart Contracts in einer Art und Weise zu schreiben, wie wir es tun, könnte schon als alte Schule angesehen werden Dezentralisierte Autonome Organisationen übernehmen bereits (mehr oder weniger) unser Verständnis von Aktiengesellschaften und Gesellschaften mit beschränkter Haftung, wie wir sie kennen. Ich habe digitale Nomaden kennengelernt, die vom Bau von Ethereum-Apps leben, und sie werden mit Ethereum und Dai (einer von Ethereum unterstützten, auf USD ausgerichteten Stablecoin) bezahlt. Das war mein persönlicher Wendepunkt: Ich musste diesen Drachen zum Fliegen bringen und ich musste ein Team dafür finden.

The ĐOor — eine Einreichung für EthBerlinZwei

Nur einen Tag später stand der EthBerlinZwei hackathon kurz vor dem Abflug und ich war bereit, Geschichte zu schreiben. Ich machte einen Vorschlag, um Leute zu finden, mit denen man sich zusammentun konnte, und fand drei erstaunliche Musketiere, die sich der Partei anschlossen: Tam (Berlin/Israel), Ben (Litauen) und Niels (Hamburg) haben die Idee fröhlich adaptiert und wir begannen mit dem Remix. Kurz gesagt, wir wollten den letzten Teil des Puzzles lösen:

Wie identifiziert man jemanden, der zu einer Veranstaltung kommt, effektiv als gültigen (und kostenpflichtigen) Teilnehmer?


Darum haben wir unser Projekt ĐOor genannt. Es ist potentiell als "Türöffner" für jede Art von physischer und nicht-physischer Zugangsbeschränkung verwendbar und besteht aus drei Teilen:

  • Veranstaltungsmanagement. Jeder kann eine neue Veranstaltung anlegen und die Teilnehmerliste der Veranstaltung, die Ticketpreise und die Inhaltsdaten kontrollieren.
  • Ticketverkauf / RSVPs. Jeder kann alle erstellten Veranstaltungen entdecken und sich für sie anmelden, indem er Ether als Ticketpreis mitschickt.
  • der Rausschmeißer-Anwendungsfall. An der Tür wird die Identität eines Teilnehmers anhand der Teilnehmerliste überprüft. Wenn sie auf der Liste steht, wird der Eintritt gewährt.

Wir haben uns wieder für die Smart Contracts von Solidity entschieden, aber wir haben dafür OpenZeppelin Tools verwendet: Sie kommen mit einer großen Anzahl von geprüften Contracts, die nachweislich als großartige Grundlage für alle gängigen Anwendungsanforderungen funktionieren. Außerdem bauten wir unser Frontend auf einfachen Web-Technologien auf und entschieden uns für die Verwendung von Vue.js als Frontend-Bibliothek (was ich persönlich wegen der unnötigen konventionellen Komplexität im Vergleich zu React für eine eher ungünstige Wahl halte; aber das ist eine ganz andere Geschichte).


Eventmanagement

Um neue Ereignisse zu erstellen, interagieren die Benutzer mit einer einzigartigen Instanz einer so genannten DoorFactory, die in ihrem Namen neue ĐOor Contracts hervorbringt. Ein Ersteller ist der erste Eigentümer dieses Vertrags, so dass alle Gebühren auf sein Konto überwiesen werden. Dank des vorgefertigten Ownable-Basisvertrags von OpenZeppelin kann ĐOors leicht auf neue Eigentümer übertragen werden. Hier sind die interessanteren Teile des Codes, beginnend mit dem Door-Werksvertrag:

pragma solidity ^0.5.0;
import "./Ownership.sol";
import "@openzeppelin/upgrades/contracts/Initializable.sol";
contract DoorFactory {
address[] public doorAddresses;
event NewDoorCreated(
address indexed doorOwner,
address indexed doorAddress,
string indexed doorName,
uint ticketPrice,
bool allowDisposeLeftovers
);
function createNewDoor(uint256 _price,
string memory eventName,
bool allowDisposeLeftovers)
public returns(address)
{
Door door = new Door();
door.initialize(_price, eventName, allowDisposeLeftovers);
door.transferOwnership(msg.sender);
doorAddresses.push(address(door));
emit NewDoorCreated(
msg.sender,
address(door),
eventName,
_price,
allowDisposeLeftovers
);
return address(door);
}
...
}

und die entstandenen Door-"Instanzen"-Contracts:

contract Door is Ownable, Initializable {
string public nameOfEvent;
uint public ticketPrice;

uint attendeesCount;
uint256 shares;
enum AttendanceTypes { NONE, REGISTERED, ATTENDED }
struct UserStruct {
AttendanceTypes ticketStatus;
}
mapping(address => UserStruct) public users;
function initialize(uint256 _price, string memory eventName)
public initializer payable
{
ticketPrice = _price;
nameOfEvent = eventName;
}

function buyEventTicket() public payable
{
require(users[msg.sender].ticketStatus == AttendanceTypes.NONE, 'User already has a ticket');
require(
msg.value == ticketPrice,
msg.value does not meet the ticket price.
);
this._owner.transfer(msg.value);
users[msg.sender].ticketStatus = AttendanceTypes.REGISTERED;
}
...
}

Teilnahme

Jede Client-Anwendung (z.B. unser Vue-Frontend) kann nun alle bestehenden Door Contracts, die erstellt wurden, über seine Vertragsadresse, die ABI des Contracts und den Aufruf seiner getAllDoors-Methode ermitteln. Sobald man sie entdeckt hat, kann man die buyEventTicket-Methode von Door aufrufen und eine entsprechende Ticketgebühr in Ethers senden. Jede Door führt eine eigene Teilnehmerliste und leitet das Geld direkt an den aktuellen Door-Besitzer weiter.

Ben führte eine Wendung in die Geschichte ein, die ein wenig tiefer geht: Wenn man eine Veranstaltung/Door als "abhebbar" markiert, müssen die Teilnehmer eine Vorauszahlung leisten, auch wenn die Veranstaltung frei ist (nicht ohne Bezug zur Idee von meetup.com). Sobald der Besitzer einen Teilnehmer als Teilnehmer der Veranstaltung identifiziert, hat der Teilnehmer das Recht, seine Anzahlung zurückzubekommen. Das ist eine ziemlich kluge Strategie, um die No-Show-Raten zu senken: Wenn Sie nicht zu einem Meetup gehen, ist Ihre Kaution verloren.

Das recht populäre "kickback" Projekt hatte diese Idee zuerst und führte eine ganze Reihe von Veranstaltungen durch, die die No-Show-Raten bereits auf ein Allzeittief senkten. Bens kleine Abhebungsmethode erlaubt es jedem Teilnehmer, seine Einlage abzuheben, nachdem er vom Gastgeber als anwesend markiert wurde und die Veranstaltung offiziell beendet ist.


Teilnahmenachweis

Und jetzt kommt der schwierige Teil. Wie kann man beweisen, dass jemand persönlich an einer Veranstaltung teilgenommen hat? Zunächst muss die Teilnehmerin an der Tür stehen und ihren Public Key irgendwie vorzeigen können, denn das ist der Datenbestand, den ein ĐOor Contract als Indikator für ihr "Ticket" speichert. Aber was hält sie davon ab, einem Freund (oder der ganzen Welt) einfach ihren Public Key zu geben? Immerhin ist er sogar in der Blockchain-History öffentlich sichtbar gespeichert, so dass jeder einfach einen öffentlichen Schlüssel, der sich angemeldet hat, aufheben und versuchen kann, mit ihm in die Veranstaltung zu gelangen.

Die einfachste Präventionsstrategie wäre es, eine öffentliche Ansprache, die an der Door vorbeigeht, als "besucht" zu markieren und jeden anderen Teilnehmer zu blockieren, der versucht, dieselbe Adresse erneut zu verwenden. Das würde jedoch bedeuten, dass wir jetzt möglicherweise den wahren Ticketinhaber ausschließen: Wer zuerst kommt, mahlt zuerst war noch nie eine gute Idee, außer in der Biologie.

Eine recht vielversprechende Variante ist die Ausgabe von ERC20 Tokens als Veranstaltungstickets. Jeder Token würde eine Eintrittskarte darstellen, und um in den Veranstaltungsort zu gelangen, überträgt man den Token einfach in die Wallet des Türstehers. Sobald diese Transaktion bestätigt ist, öffnet sich die Tür. Eine gute Idee, aber was hält jemanden davon ab, den Token einfach auf einen Freund zu übertragen? Das sollte bei kostenlosen Veranstaltungen kein Thema sein, aber für exklusive Veranstaltungen vielleicht nicht genau das, wonach man sucht. Nicht fungible ERC721 Tokens (NFTs) können dieses Problem beheben, aber sie widersprechen der Realität: Außer der aufgedruckten Nummer ist ein Ticket für die nächste Madonna-Show austauschbar. Das Gute an NFTs ist: Man könnte nahtlos nachweisen, wie es verwendet wurde, und es ist absolut einzigartig für den ersten Aussteller und Besitzer. Eigentlich eine großartige Idee für Anwendungen auf dem sekundären Ticketmarkt, aber hier ist der Vorbehalt: Die Transaktionen sind langsam.

Während es im Ropsten-Testnetz etwa 30 Sekunden dauert, bis eine Transaktion als bestätigt gelten kann, kann eine Token-Transaktion im Ethereum-Hauptnetz bis zu 2 Minuten dauern (durchschnittliche Blockzeit von 15s x 7 Bestätigungen). Wenn man auf seiner ĐOor kostenlosen Kaffee serviert, könnte das eine Option sein, aber sicherlich nicht für die Masse.
Wie sich herausstellt, hat die asymmetrische Kryptographie eine eingebaute Lösung für dieses Problem und es sind Private Key Signatures.



Sesam öffne Dich!

Hier ist also unsere Lösung: Wenn ein Teilnehmer auf ĐOor anklopft, erstellt der Türsteher eine zufällige 4-stellige Zeichenfolge und präsentiert sie dem Teilnehmer. Der Teilnehmer signiert dieses kurzlebige geteilte Geheimnis mit seinem privaten Schlüssel und erstellt einen QR-Code, der die Signatur enthält. Der Türsteher scannt den QR-Code auf dem Gerät des Gasts und ruft die ecRecover-Methode von Ethereum mit der ihm bekannten zufälligen Zeichenfolge auf.

Einchecken mit ĐOor

Diese Methode ergibt den Public Key des Teilnehmers, der in diesem Moment an der Tür steht. Er kann überprüfen, ob der öffentliche Schlüssel dem Vertrag von ĐOor bekannt ist (was eine Frage von Millisekunden ist, da er keine Statusänderung beinhaltet) und eine neue Transaktion auslösen, um den Status der Adresse auf "besucht" zu setzen. Da alle Operationen entweder außerhalb der Blockchain ablaufen oder nur den Vertragsstatus lesen, dauert der gesamte Verifizierungsprozess nicht länger als 5 Sekunden und ist noch schneller, wenn der Türsteher eine lokale Kopie aller Teilnehmeradressen behält.

Das ist die grobe Zusammenfassung dessen, was unser Team bei EthBerlinZwei erreicht hat. Unsere DevPost-Einreichung enthält Links zu allen Codes und unseren vorläufigen Demos.

Team Meeting: Eventmanagement und Discovery

Die Idee von ĐOor ist zwar ein fabelhaftes Instrument, um die "Tickets" eines Teilnehmers zu überprüfen, aber eine Frage bleibt offen: Sollten wir wirklich Veranstaltungen und ihre Metadaten auf dieser Plattform verwalten?

Wenn wir das täten, müssten Veranstalter ein Frontend verwenden, das uns gehört, um neue Veranstaltungen von einer Factory hinzuzufügen, die von uns in einem Format bereitgestellt wird, das wir uns ausdenken müssen. Kurz: Wir würden schließlich auch als das nächste meetup.com enden – mit einer dezentralisierten Datenbank, aber immer noch mit einem zentralen Konzept davon, wie Veranstaltungsdaten aussehen sollten.

Zeit für einen weiteren Hackathon: Unser völlig neues Team (mit mir als einzigem Teilnehmer, der vom Team ĐOor dazukam, die anderen sind Sascha, Jean Daniel, Sebastian und Hendrik) machte sich bei der (Link: https://diffusion.events/ text: Diffusion 2019) (20. Oktober) Gedanken darüber, wie wir Veranstaltungen auffindbar machen können, ohne sie zu besitzen oder zu definieren. Da es schwierig ist, ein völlig neues Team davon zu überzeugen, an etwas zu hacken, was ein Team zuvor hinterlassen hatte, gingen wir mit dem neuen Namen "Team Meeting" an den Start und begannen von vorne.


Nutzen, was schon da ist: das Semantic Web

Für den Datenteil kamen wir auf die Idee, mit dem loszulaufen, was bereits verfügbar ist, und wie sich herausstellt, ist die Idee eines Semantic Web sehr lebendig. Wahrscheinlich aus SEO-Gründen reichern viele Sites, die Veranstaltungsseiten anzeigen und hosten, ihr Markup mit Micro Formats oder strukturierten Daten an – kleinen, aber gut definierten Informationen, die entweder als zusätzliche Attribute innerhalb des Markups, als Meta-Header oder als Meta-Blöcke in Skript-Tags versteckt und als JSON-LD formatiert sind.

Ein Browser, der eine Seite besucht, die schema.org Microdata Formats enthält, erkennt sofort, dass der Benutzer eine Ereignisseite durchsucht und alle Metadaten daraus extrahieren könnte: Titel, Veranstaltungsort oder Öffnungszeiten sind alle in einem maschinenlesbaren Format verfügbar. Gut für uns, dass meetup.com und Eventbrite das stark ausnutzen: Sie stellen viele Veranstaltungsinhalte auf ihren Veranstaltungsseiten aus, in der Hoffnung, dass der GoogleBot sie sinnvoll nutzen kann. Hier ist ein Beispiel für JSON-LD-formatierte Metadaten auf Eventbrite für den kommenden React.Day:

Warum also sollte GoogleBot der einzige Client sein, der diese Daten sinnvoll nutzt?

Wir beschlossen, eine Chrome-Erweiterung zu schreiben, die Mikrodatenformate erkennt und Ereignisdaten aus Seiten extrahiert. Sobald sie ein Ereignis erkennt, bietet sie dem Benutzer eine "RSVP"-Aktion an (oder möglicherweise eine "Ticket kaufen"-Schaltfläche, wenn das Ereignis nicht kostenlos ist). Sobald ein Benutzer auf diese Schaltfläche klickt, werden die Veranstaltungsdaten an einen freien, dezentralisierten und globalen Speicher übertragen: ipfs.io.

Dank der Alpha-Version von ipfs-js, die ohne die Notwendigkeit funktioniert, einen eigenen oder einen dedizierten IPFS-Knoten zu betreiben – die Peer-Erkennung und das Hochladen geschieht nur im Browser (ipfs-js hält nur einige konstante Root Nodes, um Peers zu entdecken). Einmal auf IPFS erhält jedes Ereignis einen eindeutigen Inhaltsbezeichner, das CID. Nach einigen Sekunden werden die Informationen auf dem weltweiten dezentralen Speichernetzwerk gespeichert (z.B. https://ipfs.io/ipfs/QmevnNxM2qTRJp6DFdq7Xh2g5GoMXWLoNGAKSjTtKQRv1w).

Da wir nicht auf einen MetaMask / Web3-Kontext aus dem Bereich der Browser-Erweiterung zugreifen können, leiten wir den Benutzer auf eine statisch gehostete Đapp Landing Page um, die einen Web3-Kontext anfordert und mit der Original-URL der Ereignisseite und ihrem IPFS-CID ausgestattet ist. Hier kann sich der Benutzer schließlich für das Ereignis anmelden, indem er eine kleine Transaktion an die rsvpForEvent-Methoden von dedizierten Smart Contracts sendet:

pragma solidity ^0.5.8;
contract Meeting {
struct EventStruct {
address[] rsvps;
bytes cid;
bool isCanceled;
}
mapping(bytes32 => EventStruct) public events;
mapping(bytes32 => uint8) public meetups;
mapping(bytes32 => uint8) public attendees;
event MeetupCreated(string url, bytes cid);
event MeetupRSVPee(string url, address attendee);
function rsvpForEvent(string memory url, bytes memory cid) public {
bytes32 id = keccak256(abi.encode(url));
meetups[id] = meetups[id] + 1; // cheap existence check
if (meetups[id] > 1) {
events[id].rsvps.push(msg.sender);
} else {
emit MeetupCreated(url, cid);
}
bytes32 attend = keccak256(abi.encode(url, msg.sender));
attendees[attend] = 2; // code for attending
emit MeetupRSVPee(url, msg.sender);
}
function rsvpForExistingEvent(string memory url) public {
bytes32 id = keccak256(abi.encode(url));
require(meetups[id] > 0, "event needs to be created first");
bytes32 attend = keccak256(abi.encode(url, msg.sender));
attendees[attend] = 2;
emit MeetupRSVPee(url, msg.sender);
}
function isAttending(string memory url) public view returns (bool attending) {
attending = isOtherAttending(url, msg.sender);
}
function isOtherAttending(string memory url, address identity) public view returns (bool attending) {
bytes32 attend = keccak256(abi.encode(url, identity));
attending = attendees[attend] == 2;
}
function isRegistered(string memory url) public view returns (bool registered) {
bytes32 id = keccak256(abi.encode(url));
registered = meetups[id] > 0;
}
function countAttendees(string memory url) public view returns (uint users) {
bytes32 id = keccak256(abi.encode(url));
users = meetups[id];
}
}

Das ist ein mehr oder weniger funktionierender Contract, um neue Veranstaltungen und deren Teilnehmerliste im Ledger zu verankern. Was fehlt, ist der Entdeckungsteil (zeige mir Veranstaltungen in der Nähe von 'Berlin'). Dafür mussten wir etwas tiefer in die Geheimniskiste greifen:

The Graph: Ableitung einer GraphQL-API aus Ethereum-Events

Angenommen, man möchte Ereignisse in der Nähe nur durch Iteration von Ethereum-Transaktionen finden und deren verwandte Inhalte einzeln abrufen. Wenn die Menge der gespeicherten Ereignisse groß wird, wäre es ein massiver Aufwand, dies bei jeder Anfrage zu tun, und deshalb muss man sich eine (möglicherweise zentralisierte) Projektion ausdenken, die den erzeugten Zustand des Ereignisspeichers in einer abfragbaren Datenbank fortsetzt. Wenn wir die Ereignisdaten nur auf IPFS indizieren würden, könnte OrbitDB eine geeignete Option sein, aber da wir dabei sind, die Projektion zu aktualisieren, sobald ein RSVP auftritt, haben wir uns für The Graph entschieden.

GraphQL API inferred by events emitted by our smart contract

The Graph ist ein YML-konfigurierter GraphQL-SaaS / selbst gehosteter Dienst, der auf Events auf Ethereum und IPFS hört und seinen intern persistierenden Zustand gemäß den Eventdaten aktualisiert. Sie können Handler und Mappings geschrieben werden, die aufgerufen werden, sobald ein Ereignis ausgelöst wird, und innerhalb dieser kann man fast alles tun, was man brauchen, um Daten abzurufen, die mit dem Ereignis in Zusammenhang stehen. Die Graph-Mappings sind in AssemblyScript geschrieben, einer Untermenge von TypeScript, und zu einer ausführbaren WASM-Binärdatei kompiliert.

Es gibt zwei obligatorische Teile, um eine Graph-Instanz zu konfigurieren und zu erstellen: Zuerst muss man eine GraphQL-Schema-Definition für die projizierten Entitäten definieren. Der Graph leitet eine ORM-ähnliche Entitätsschicht ab, die man innerhalb Ihrer Handler zum Lesen und Schreiben von Entitäten in die von The Graph bereitgestellte "Speicher"-Abstraktion verwenden können. Der wichtigste Teil ist die "Subgraph"-Konfiguration, die The Graph verwendet, um Ereignis-Listener für Ihre intelligenten Verträge einzurichten und Ihre benutzerdefinierten Handler an Ereignisse und Entitäten zu binden:

specVersion: 0.0.2
schema:
file: ./schema.graphql
dataSources:
- kind: ethereum/contract
name: Contract
network: ropsten
source:
address: "0xe742EF468584506C32B86541d0c3d4878857Af66"
abi: Contract
mapping:
kind: ethereum/events
apiVersion: 0.0.3
language: wasm/assemblyscript
entities:
- MeetupCreated
- MeetupRSVPee
abis:
- name: Contract
file: ./abis/Contract.json
eventHandlers:
- event: MeetupCreated(string,bytes)
handler: handleMeetupCreated
- event: MeetupRSVPee(string,address)
handler: handleMeetupRSVPee
file: ./src/mapping.ts

Bei dieser Konfiguration richtet The Graph Dienste ein, die MeetupCreated- oder MeetupRSVPee-Events des Contracts abhören und alle diese Daten in eine gehostete Datenbank (die unter der Haube tatsächlich Postgresql verwendet) mit einem automatisch generierten GraphQL-API einspeisen, die darüber liegt. Das Frontend kann einfach nach tiefen Grafikdaten fragen, z.B. um anstehende Veranstaltungen mit bestimmten Tags in einer bestimmten Stadt zu filtern.

Unsere letzte Devpost-Einreichung für "Team Meeting" kann hier gefunden werden. Derzeit befindet sich der Code noch im Hackathon-Zustand, funktioniert aber mehr oder weniger wie oben erklärt.

Konsolidierung & Schlussfolgerung

Was wir entwickelt haben und was wir im Begriff sind zu bauen

All der oben genannte Codes und die Informationen sind das Ergebnis von drei Wochenenden ohne Schlaf von etwa 10 Personen, die sich noch nie in ihrem Leben getroffen haben. Bitte habt also Geduld mit uns: Dies ist noch keine funktionierende Software.

Zum Zeitpunkt des Schreibens sind wir gerade dabei, herauszufinden, wie wir dieses Projekt weiterführen. Wir bleiben höchstwahrscheinlich auf der Ethereum-Chain, obwohl eine lebhafte Diskussion darüber geführt wird, ob Parity / Substrate, NEAR, elastic Eth Sidechains oder Eth2.0 / Serenity die Stacks sind, auf die man warten oder auf denen man aufbauen kann. Persönlich würde ich gerne beim bewährten Ethereum-Stack bleiben und MetaMask als "Identitäts"-Wallet und obligatorische Abhängigkeit für Interaktionen annehmen.

Wir werden sicherlich etwas Energie in den Ereigniserkennung / Speicherung / Entdeckung Teil der ganzen Geschichte investieren, da dies der am meisten prioritäre, föderierte und in Silos gestellte Teil des derzeitigen Ökosystems der Meetups/Events ist. Die Leute, die hinter unserer ĐOor Idee stehen, wollen noch einen Schritt weiter gehen und weitere, ereignisnahe Features hinzufügen (z.B. Kickback-ähnliche RSVP-Einlagen, die nach dem Nachweis der Teilnahme an ihren Ursprung zurücktransferiert werden), während Team Meeting es vorzieht, die Funktionalität so weit wie möglich zu entkoppeln, um nur mit der einfachen, in der Chain verankerten Ereignisdatenbank zu helfen.

Wer Teil der Diskussion sein möchte oder sich durch unseren Code wühlen oder zu ihm beitragen will, oder wer ihn abspalten und ein eigenes neues meetup.com aufbauen möchte: nur zu, der Weg ist frei – hier ist unsere "ĐOor" Organisation, die derzeit sowohl unser ĐOor als auch unser Team Meeting Repository pflegt: github.com/d0or . Wer uns in irgendeiner Weise unterstützen oder integrieren möchte, bitte keine Scheu. Nehmt Kontakt mit uns auf!