WASM auf der Blockchain: Das geringere Übel

Dieser Artikel wurde von am 19.11.2018 Jack Ransham aka @meatwithdreams auf Medium veröffentlicht.

https://medium.com/polkadot-network/wasm-on-the-blockchain-the-lesser-evil-da8d7c6ef6bd

Am Anfang war der Bitcoin.

Bitcoin wurde 2009 oder so ähnlich veröffentlicht und löste den Blockchain-Wahn aus, der nun die Technologiebranche überrollt hat. Es war nicht die erste Blockchain, es war nicht der erste Einsatz von Kryptografie zur Sicherung eines Geldsystems, es war nicht einmal der erste Einsatz eines Hashing-Proof-of-Work-Algorithmus, aber es war eine brauchbare und praktische Umsetzung all dieser Dinge, die von echten Menschen genutzt werden konnte. Bitcoin gilt als der Beginn dessen, was in der Branche als Kryptowährungen der "ersten Generation" bezeichnet wird, d.h. Kryptowährungen, die keine On-Chain-Scripting-Fähigkeit haben. Dies ist technisch gesehen nicht der Fall, da sogar die allererste Version von Bitcoin über On-Chain-Skripting-Fähigkeiten verfügte, aber die Bitcoin-Skripting-Plattform ist und war immer totaler Mist, so dass die Gründung von Kryptowährungen der "zweiten Generation" im Allgemeinen Ethereum zugeschrieben wird.

Noch einmal: Ethereum war nicht die erste Kryptowährung mit einer Smart-Contract-Plattform, aber es war die erste praktische Implementierung, die von echten Menschen genutzt werden konnte. Auch wenn Blockchains der zweiten Generation als absolut überlegen gegenüber Blockchains der ersten Generation gelten, sind die meisten modernen Blockchains immer noch Blockchains der ersten Generation, die keine oder nur minimale Skripting-Funktionen enthalten. Einige der größten Kryptowährungen hinter Bitcoin und Ethereum - Monero, ZCash, Dash zum Beispiel - unterstützen keinerlei Skripting, sogar weniger als Bitcoin. Das liegt daran, dass das Schreiben einer Smart-Contract-Plattform, gelinde gesagt, verdammt schwer ist.

Schauen wir uns einige Anforderungen an, die eine virtuelle Blockchain-Maschine (VM) erfüllen muss:

  • Sicher - es muss unmöglich sein, dass jemand durch die Nutzung der Smart-Contract-Plattform gegen seine Rechte verstößt. Schließlich kann jeder einen Smart Contract einrichten.
  • Deterministisch - es muss unmöglich sein, dass Smart Contracts auf verschiedenen Nodes unterschiedliche Ergebnisse liefern, selbst bei unterschiedlichen Architekturen, Betriebssystemen, Zeitzonen, Mondphasen usw.
  • Generisch - es sollte möglich sein, mit den von der VM bereitgestellten Primitiven im Grunde alles zu schreiben, was man will. Einige Leute würden dies für ein falsches Merkmal halten, ich werde meine Argumente später erläutern.
  • Effizient - je mehr CPU-Zeit ein bestimmter Contract zur Ausführung benötigt, desto teurer wird die Ausführung dieses Contracts. Dies scheint kein allzu großes Problem zu sein, da niemand Raytracing oder andere CPU-gebundene Operationen auf der Blockchain ausführt, aber es führt zu einer Kaskade von anderen Problemen, wie wir später sehen werden.

Einige davon sind ähnlich wie bei Nicht-Blockchain-VMs, aber andere Bedenken sind eher esoterisch. "Normale" VMs müssen im Allgemeinen nicht deterministisch sein, und viele von ihnen würden Determinismus als Fehlfunktion betrachten (was es beispielsweise unmöglich macht, Zufallszahlen zu erzeugen). Viele VMs müssen nicht einmal sicher sein, da sie im Allgemeinen als vertrauenswürdiger Code angesehen werden. Sie müssen nur sicherstellen, dass der Benutzer durch seine Eingaben keine Rechte verletzen kann, nicht aber, dass der Code seine Rechte nicht verletzen kann - das wird vom Betriebssystem sichergestellt. Die Ausnahme zu diesem zweiten Punkt ist die Webplattform, zu der ich gleich noch komme.

Schauen wir uns also die Ethereum-VM an, die derzeit bei weitem am häufigsten verwendete Blockchain-VM. Sie ist sicher, da es unmöglich ist, Ihre Berechtigungen durch das Schreiben von Code zu verletzen. Sie ist deterministisch, denn es ist nicht möglich, Code zu schreiben, der auf verschiedenen Nodes unterschiedlich läuft. Es ist jedoch nicht besonders generisch oder effizient. Es ist im wahrsten Sinne des Wortes generisch, da es Turing-komplett ist, aber jeder, der schon einmal vom Turing-Tarpit gehört hat, weiß, dass dies weder notwendig noch ausreichend ist. Die Wahrheit ist, dass seine geringe Effizienz de facto zu einem Versagen des Gattungsprinzips führt. Es ist einfach unpraktisch, viele Programme zu schreiben, da der Betrag, den ein Benutzer zahlen müsste, um die Software zu nutzen, es wirtschaftlich nicht machbar macht.

Aus diesem Grund wurden der virtuellen Maschine im Laufe der Jahre zusätzliche integrierte Funktionen hinzugefügt, die effiziente Versionen häufig benötigter Funktionen implementieren. Im Moment handelt es sich dabei ausschließlich um kryptografische Funktionen, da diese die größten Leistungskiller in der EVM sind. Das liegt zum Teil daran, dass die Wortgröße des EVM 256 Bit beträgt und damit groß genug ist, um in Software implementiert werden zu müssen. Obwohl wir bei Parity viel Arbeit in die Optimierung unserer 256-Bit-Integer-Implementierung gesteckt haben, ist diese Software-Implementierung immer noch um Größenordnungen langsamer als (zum Beispiel) die 32-Bit-Hardware-Arithmetik.

Ein weiteres Problem bei der effizienten Ausführung von EVM-Code ist, dass er interpretiert werden muss. Eine gängige Methode zur massiven Erhöhung der Geschwindigkeit virtueller Maschinen ist die "Just-in-Time-Kompilierung". Das bedeutet, dass sie während der Ausführung in nativen Code kompiliert werden. Bei der Blockchain ist das nicht so einfach. Das übergreifende Problem bei Blockchain-VMs im Vergleich zu normalen VMs besteht darin, dass Blockchain-VMs den Code, den sie ausführen, jederzeit als feindlich betrachten müssen, denn da es sich um ein Finanzsystem handelt, kann jedes Problem wahrscheinlich von jemandem zum finanziellen Vorteil genutzt werden. Bei der JIT-Kompilierung werden durch die Kompilierung in nativen Code im Wesentlichen alle Sicherheitsvorkehrungen entfernt, und man muss extrem vorsichtig sein, um größere Fehler zu vermeiden, von Konsensproblemen aufgrund leicht unterschiedlichen Verhaltens auf verschiedenen Plattformen bis hin zur Ausführung von beliebigem Code.

Nehmen wir jedoch an, Sie hätten einen perfekten JIT-Compiler geschrieben. Einen ohne Bugs. Selbst dann könnte es Probleme geben, und deshalb müssen wir uns fragen, was die meisten JITs so schnell macht. Sicherlich gibt es einen Geschwindigkeitsschub durch die Verwendung von nativem Code, aber die wahre Stärke eines JIT liegt in der Optimierung. Man kann etwas, das in einem portablen Bytecode vorliegt, in etwas konvertieren, das für die jeweilige Plattform optimiert ist. Der EVM wurde als Backend für Solidity entwickelt und nicht, um der Funktionsweise einer Maschine nahe zu kommen, daher ist für eine effiziente Kompilierung eine Optimierung erforderlich. Um zu verstehen, warum dies ein Problem ist, müssen wir über Komplexität sprechen.

Komplexität ist die Untersuchung der Zeit, die ein Code im Verhältnis zur Größe der Eingabe benötigt. Nehmen wir an, Sie haben einen Algorithmus, der mit einer Liste arbeitet. Wenn er unabhängig von der Länge der Liste die gleiche Zeit benötigt, ist er O(1) - konstante Zeit. Wenn er die doppelte Zeit für die doppelte Länge der Liste benötigt, ist er O(n) - lineare Zeit. Wenn er für die doppelte Länge der Liste die vierfache Zeit benötigt, ist es O(n^2) - polynomielle Zeit. Für die Blockchain-VM muss jeder vollständig validierende Node die Kompilierungsarbeit leisten, und wenn Sie einen Contract bereitstellen wollen, müssen Sie für diese Kompilierungsarbeit bezahlen - andernfalls bestünde ein Angriffsvektor darin, viele Contracts bereitzustellen, die teuer zu kompilieren sind. Mit einem Algorithmus mit linearer Zeit (oder besser) können wir den Preis nach der Anzahl der Bytes im Contract berechnen. Wir wissen, dass der Algorithmus eine Zeitspanne benötigt, die proportional zur Anzahl der Bytes ist, also können wir die Anzahl der Bytes als Schätzwert für die Zeitspanne verwenden. Für nichtlineare Algorithmen ist dies unmöglich.

Natürlich kann man für einen O(n^2)-Algorithmus den Preis in Bytes zum Quadrat angeben, aber die Wahrheit ist, dass wir die Komplexität hinreichend komplexer Algorithmen oft nicht wirklich kennen, was bei Optimierungen fast immer der Fall ist. Wir müssen ihn so einfach halten, dass wir uns der Komplexität sicher sein können, und das Erzwingen eines linearen Algorithmus sorgt dafür, dass die Contract-Bereitstellung billig bleibt, während wir gleichzeitig die Bereitstellung leicht bepreisen können. Außerdem wirft diese Komplexitätsbeschränkung die JIT-Compiler aus dem Fenster - man kann sich einen JIT-Compiler wie einen AOT-Compiler vorstellen, mit dem Unterschied, dass er nicht strikt, sondern träge ist, und träge Algorithmen sind bekanntermaßen schwer zu berechnen. Es ist einfacher, den Contract einmal zu kompilieren, wenn man ihn zum ersten Mal sieht. Optimierungen sind in Ordnung, solange sie in linearer Zeit erfolgen.

Bei Parity Technologies arbeiten wir an der Entwicklung und dem Aufbau von Polkadot, einer "Blockchain der Blockchains". Wir betrachten diese und ähnliche Projekte als die dritte Generation von Blockchains - mit dem Schwerpunkt auf Interoperabilität. Anstelle von Smart Contracts haben wir Parachains. Komplette Blockchains, die als Anwendungen laufen. Dafür brauchen wir eine bessere virtuelle Maschine als EVM, und vor allem brauchen wir etwas, das effizienter ist. Wir haben uns für WebAssembly (WASM) entschieden. Dabei handelt es sich um eine Spezifikation für virtuelle Maschinen, die der Semantik physischer Maschinen in der realen Welt von heute entsprechen soll, so dass sie auf moderner Hardware effizient ausgeführt werden kann. Sie ist außerdem so aufgebaut, dass sie leicht überprüfbar ist, nicht unbedingt im Hinblick auf logische Korrektheit, sondern auf Speichersicherheit. Es gibt zum Beispiel keine zufälligen Go-To’s, sondern man muss eine tatsächliche Funktion angeben, die man aufrufen oder aus der man ausbrechen will. Es soll auch kein undefiniertes Verhalten geben. Das bedeutet, dass Sie den VM-Code naiv in nativen Code kompilieren können, ohne teure Laufzeitprüfungen einfügen zu müssen.

Wir werden oft danach gefragt, da einige der Meinung sind, dass die Verwendung einer universellen virtuellen Maschine ein schlechter Schritt ist. Dies wurde uns von Mozilla-Mitbegründer Brendan Eich in diesem Tweet mitgeteilt. Um sein Argument auf die, wie ich finde, wohlwollendste Art und Weise zusammenzufassen, ist er der Meinung, dass man beim Aufbau einer Smart-Contract-Plattform erzwingen sollte, dass alle Contracts eine unvollständige Turing-Sprache verwenden, die einer formalen Überprüfung zugänglich ist, um die Korrektheit zu überprüfen. Turing-Vollständigkeit ist ein interessantes Thema, aber für dieses Argument können Sie sich eine unvollständige Turing-Sprache als eine Sprache vorstellen, die keine unendlichen Wiederholungen oder Endlosschleifen zulässt. Man kann immer wissen, ob das Programm bei einer bestimmten Eingabe fertig wird oder nicht - es wird immer fertig. Dadurch lassen sich bestimmte Garantien für die Ausführung des Programms leichter geben. Man kann immer eine Obergrenze für die Zeit angeben, die für die Ausführung eines bestimmten Auftrags benötigt wird.

Ich denke, dass seine Logik in gewisser Hinsicht richtig ist - formale Verifizierung ist wichtig und sollte im Blockchain-Bereich gefördert werden -, aber er vertritt einen extremen Standpunkt, der keinen Pragmatismus zulässt. Sicher, man kann die Verwendung einer bestimmten Programmiersprache erzwingen, indem man die Contracts in dieser Sprache direkt auf der Blockchain bereitstellt (statt in einem Bytecode einer virtuellen Maschine) oder in einer komprimierten Form wie einem seriellen AST. Dies würde eine formale Überprüfung bestehender Contracts ermöglichen, aber dann muss man entweder einen extrem teuren AST-Walking-Interpreter verwenden oder in den Nodes zu Bytecode kompilieren, was bedeutet, dass man auf das oben erwähnte Problem stößt - die Kompilierung einer komplexen Sprache ist nicht linear, und dann muss man die Bereitstellung mit Transaktionskosten bepreisen, dem Preisfindungsmechanismus, der für die Ausführung von Smart Contracts in Ethereum verwendet wird, bei dem man die Kosten der Ausführung bei der Ausführung addiert. Das ist fehleranfällig und teuer. Ich bin mir nicht sicher, ob Eich dies vorschlägt, weil seine Argumentation durch die Twitter-Zeichenzählung abgekürzt ist, aber zumindest schlägt er ein maßgeschneidertes Turing-incomplete VM Bytecode-Format mit einer maßgeschneiderten funktionalen Programmiersprache darauf vor.

Das erste Problem, das unserer Meinung nach von WASM gut gelöst wird, ist, dass alles, was maßgeschneidert ist, ein Problem darstellen wird. Die dApp-Entwicklungsbranche ist so klein, dass sie fast unbedeutend ist. Ethereum ist die größte Smart-Contract-Plattform der Welt, und es gibt nur eine einzige De-facto-Programmiersprache - Solidity. Solidity gibt es schon so lange wie Ethereum, und dennoch ist das Tooling dafür gleichwertig oder sogar schlechter als bei Sprachen vor 1.0 wie Zig, die huckepack auf dem bestehenden Tooling für LLVM und Assembler aufbauen können. Ethereums Verwendung einer benutzerdefinierten VM bedeutet, dass wir bei Null anfangen müssen. Es gab bis zu diesem Jahr (2018 zum Zeitpunkt der Erstellung dieses Artikels) keinen Debugger, 3 Jahre nach der Veröffentlichung von Ethereum. Eine neue maßgeschneiderte VM und Sprache bedeutet, dass Sie dies erneut tun müssen. Bedenken Sie, dass dies für die größte Smart-Contract-Plattform gilt. Das Tooling folgt der Popularität, nicht der Qualität, wie die hervorragende Tooling-Unterstützung für JavaScript bezeugen kann.

Nehmen wir also an, dass die Turing-Unvollständigkeit eine gute Sache ist und dass man Smart Contracts in einer Turing-unvollständigen, leicht überprüfbaren Sprache schreiben sollte. Warum sollte man eine eigene Sprache entwickeln? Warum nicht, sagen wir, Idris verwenden. Sie können Idris bereits heute mit seinem C-Backend nach WASM kompilieren. Die Turing-Vollständigkeit der Spezifikation der virtuellen Maschine schließt nicht aus, dass man eine unvollständige Turing-Sprache darauf aufbaut. Man müsste eine idiomatische Bibliothek für die Erstellung von Smart Contracts erstellen, aber das ist viel einfacher als eine neue virtuelle Maschine und Sprache zu entwickeln. Das Einzige, was man verlieren würde, ist die Möglichkeit, bereits eingesetzte Contracts formal zu verifizieren, wobei die Verifizierung eines Contracts nicht bedeutet, dass er korrekt ist. Es bedeutet nur, dass er die Bedingungen erfüllt, die er sich selbst auferlegt. Diese Einschränkungen machen es wesentlich einfacher, korrekten Code zu schreiben, aber nicht wesentlich einfacher, bestehenden Code zu validieren, ohne dass ein menschliches Eingreifen erforderlich ist.

Die Tatsache, dass Idris bereits nach WASM kompiliert, deutet auf einen weiteren Grund für die Verwendung von WASM hin - so gut wie alles lässt sich damit kompilieren. Wir schreiben bereits produktionsreife, hochzuverlässige Systeme wie Betriebssystem-Kernel und Kryptowährungen in Sprachen wie C, C++ und Rust, die alle über exzellente Werkzeuge und sogar ein gewisses Maß an automatisierter Verifizierungsinfrastruktur verfügen, während sie es auch einfach machen, extrem schnellen Code zu schreiben. Alle diese Sprachen lassen sich auch nach WASM kompilieren. Ganz zu schweigen von wirklich hochintegrierten, leistungsstarken Sprachen wie SPARK, die in der Flugsoftware von Flugzeugen verwendet werden und die in Zukunft mit dem WASM-Backend von GCC ebenfalls nach WASM kompiliert werden können (Ada, von dem SPARK eine Teilmenge ist, kann mit GCC kompiliert werden).

Ein Vorteil der Turing-Unvollständigkeit ist, wie ich bereits sagte, dass man eine Obergrenze für die Programmausführungszeit haben kann. Theoretisch bedeutet dies, dass man nicht den "Gaspreis"-Mechanismus von Ethereum verwenden muss, bei dem jede Anweisung einen Preis hat und die Kosten der gesamten Ausführung nach und nach aufgerechnet werden. Wenn Sie jedoch den Preis nach dieser Obergrenze berechnen, wird die große Mehrheit der Contracts weit mehr kosten als die tatsächliche Zeit, die für die Ausführung des Contracts benötigt wird. Infolgedessen werden Sie wahrscheinlich eine Art Transaktionspreis implementieren wollen, um es für die Benutzer billiger zu machen - warum sich also mit Turing-Unvollständigkeit in die Knie zwingen, wenn Sie es sowieso wie eine Turing-komplette virtuelle Maschine bepreisen müssen.

Den coolsten Teil von WebAssembly in Substrate habe ich mir für den Schluss aufgehoben: Es ermöglicht uns, eine WASM-Implementierung der Blockchain-Logik auf der Blockchain selbst einzusetzen. Durch die Verwendung einer virtuellen Maschinenspezifikation, mit der C++ und Rust effizient kompiliert werden können, können wir die Blockchain-Logik einmal schreiben und sie sowohl in nativen Code für die ausführbare Client-Software als auch in WASM kompilieren, um sie auf der Blockchain einzusetzen. Das bedeutet, dass selbst wenn ein Client nicht aktualisiert wird, er nicht anfängt, neue Blöcke abzulehnen, die von aktualisierten Clients erzeugt wurden - alle aktualisieren die Laufzeit im Gleichschritt. Wenn Sie nie ein Client-Update erhalten, werden Sie die langsamere WASM-Version verwenden, und Sie werden einen netten Geschwindigkeitsschub erhalten, wenn Sie ein Update erhalten (und Sie werden Updates für die Teile des Clients erhalten, die den Konsens nicht beeinflussen). Dies vermeidet das Problem von Ethereum, jedes Mal einen Hard-Fork durchführen zu müssen, wenn sie upgraden wollen, und das Problem von Bitcoin, aus Angst vor Hard-Forks Updates komplett zu vermeiden, außer unter extremen Umständen. Ich denke, dass dies sehr cool ist, aber es ist kein ausreichender Grund, da man diese WASM-Laufzeit von der Smart Contract VM trennen könnte, wenn man das wollte.

Nachdem ich nun also WebAssembly gründlich verteidigt habe, lassen Sie uns über die Implementierung sprechen. Eine Frage, die wir oft hören, ist, dass wir V8 oder SpiderMonkey verwenden sollten, eine WASM-Engine von der Stange, die von einem Drittanbieter geschrieben wurde. Man sollte meinen, dass wir, da wir gerne auf den Schultern von Giganten stehen, voll und ganz dafür wären, aber leider ist es nicht möglich, diese zu verwenden. Schauen wir uns die Anforderungen an eine Blockchain-VM noch einmal an:

  • Sicher
  • Deterministisch
  • Allgemein
  • Effizient

V8 und SpiderMonkey sind sicherlich sicher, generisch und effizient, aber es gibt ein Problem, wenn es um Determinismus geht. Es ist zwar wahrscheinlich, dass sie Determinismus anstreben, aber ich habe keinen Zweifel daran, dass sie den Determinismus in obskuren Eckfällen im Interesse der Effizienz aufgeben würden, solange die WASM-Spezifikation dies zulässt. Für Blockchain können wir das nicht zulassen. Selbst die obskursten Eckfälle müssen über alle Architekturen hinweg präzise emuliert werden, da ein Angreifer dies sonst absichtlich ausnutzen kann, um einen Fork oder eine Abschaltung des Netzwerks zu erzwingen. Auch wenn V8 und SpiderMonkey deterministisch sind, handelt es sich immer noch um optimierende Compiler, und zwar JIT-Compiler. Wie ich bereits erwähnt habe, sind Optimierer nichtlinear und JITs sind schwer zu berechnen, so dass es keine einfache Möglichkeit gibt, den Preis für die Bereitstellung eines Contracts zu bestimmen. V8 und SpiderMonkey sind nicht nur theoretisch nichtlinear: In der Praxis wurden "Compiler-Bomben" (Codestücke, die den Compiler zu einer exponentiell langen Zeitspanne veranlassen) gefunden, und es gibt keinen Grund zu der Annahme, dass, selbst wenn sie behoben sind, in Zukunft nicht noch mehr gefunden werden. Das Problem des Determinismus ist meiner Meinung nach überwindbar, aber das Problem der Komplexität ist schwieriger zu lösen. Wenn wir eine Lösung dafür finden, ist es möglich, dass eine künftige Version von Substrate V8 oder SpiderMonkey als WASM-Laufzeitumgebung verwenden kann, aber die Gefahr, dass böswillige Akteure Probleme in der Laufzeitumgebung nutzen, um das System auszunutzen, bedeutet, dass wir vorsichtig sein müssen.

Wenn wir also einen WASM-to-native-Compiler erstellen wollen, müssen wir das selbst tun, mit starken Garantien für Laufzeitverhalten und Determinismus. Das ist nicht einfach, aber in der Zwischenzeit haben wir einen effizienten Wasm-Interpreter namens wasmi. Dies ist ein korrektheitsorientierter Interpreter für WASM auf der Blockchain, nicht leistungsorientiert, aber er ist bereits schnell und wir arbeiten ständig daran, seine Leistung zu verbessern.

Zusammengefasst sind das die wichtigsten Punkte:

  • WASM ermöglicht uns die Wiederverwendung vorhandener Werkzeuge;
  • WASM ist leicht auf seine Korrektheit überprüfbar;
  • WASM ermöglicht es uns, den Preis für die Bereitstellung und den Preis für die Ausführung effektiv abzuwägen;
  • WASM ermöglicht es uns, isomorphen Blockchain-Logikcode für fork-freie Upgrades zu haben;
  • Die Vorteile der auf formale Verifizierung ausgerichteten Option lösen sich auf, wenn man versucht, sie auf ein VM-Format anzuwenden - eine Turing-unvollständige Sprache eignet sich besser als Frontend, das von Menschen verwendet wird, als ein Backend, das vom Blockchain-Client interpretiert wird;
  • Wir können nicht einfach bestehende WASM-Laufzeiten verwenden, da unsere Anforderungen viel strenger sind als die der anderen.

Ich hoffe, dass ich die meisten Fragen zu unserer Entscheidung für WASM und unsere anschließende Entscheidung gegen die bestehenden WASM-Engines im Kern unseres Parity Substrate-Produkts (und anschließend in Polkadot) beantwortet habe. Wenn Sie weitere Fragen haben, können Sie diese an meinen Twitter-Account @meatwithdreams schicken.

Sie können diesen Artikel in seiner ursprünglichen Form in meinem Blog lesen.

Dieser Artikel wurde übersetzt in Kooperation mit Wagmedia (Twitter: @thatmediawag).

0
MannIm_MondPost author

@mannim_mond 3p17JJF1notmfv2GaBJWMz12LSQEwnzoThCf6XQDL4ERhc5H

Das ist der offizielle WagMedia Space Germany! Hier werden interessante und lesenswerte DotSama-Artikel durch die Wag-Media community übersetzt und öffentlich zur Verfügung gestellt. Mitmachen? Trete unserem Discord bei und werde Teil der größten News Community im DotSama Universum.

0 comments

Das ist der offizielle WagMedia Space Germany! Hier werden interessante und lesenswerte DotSama-Artikel durch die... Show More