You can also read the English version of this article.
Teil 1: Wie funktioniert Bitcoin Script, und warum ist es so komplex?
Teil 2: Was ist Miniscript, und wie macht es Bitcoin Scripting einfacher?
Teil 3: Einen Miniscript-Parser und -Analysator in Go schreiben

Im ersten Teil dieser Artikelserie haben wir uns Beispiele dafür angesehen, wie man Bitcoins mit Hilfe von Bitcoin Script, der nativen Skriptsprache von Bitcoin, mit komplizierten Bedingungen wie Person1 kann jederzeit ausgeben ODER (Person2 kann in einem Jahr ausgeben) sperren kann. Diese Beispiele zeigen, wie schwierig es für Entwickler ist, solche Bedingungen zu erstellen und sie in der Praxis anzuwenden. Es war schwierig, ein Skript zu entwickeln, das die gewünschten Ausgabebedingungen kodiert, und gleichzeitig gültige Witnesses zu erstellen, die die Bitcoins ausgeben können.

Miniscript löst diese Probleme, indem es Fragmente einführt, die Teile des Bitcoin-Skripts beinhalten. Dadurch wird es einfacher, komplexere Bitcoin-Skripte zu schreiben, indem die Fragmente zusammengesetzt werden.
In diesem Artikel erfährst du, wie Miniscript im Detail funktioniert und wie es auf Bitcoin Script übertragen wird.

Bitte nimm dir die Zeit, den ersten Teil zu lesen, bevor du einsteigst, da wir uns auf die dort vorgestellten Bitcoin-Skript-Konzepte beziehen, z.B. wie Bitcoin-Skripte ausgeführt werden, um festzustellen, ob ein Coin ausgegeben werden kann.

Was ist Miniscript?

Miniscript besteht aus zwei unabhängigen Teilen:

Policy-Sprache

Die Policy-Sprache ist eine High Level Programmiersprache, die komponierbar und für Menschen leicht zu lesen und zu schreiben ist. Die Policy-Sprache kann von einem Policy-Compiler automatisch in ein geeignetes Miniscript übersetzt werden. Ein Beispiel für eine Richtlinie ist or(pk(key_1),pk(key_2)), was bedeutet "entweder key_1 oder key_2 kann ausgeben".

Miniscript

Ein Miniscript-Ausdruck kann zum Beispiel so aussehen: or_b(pk(key_1),s:pk(key_2)), welcher mit sipa's compiler aus der obigen Policy zusammengestellt wurde. Sie ist eine Komposition aus Fragmenten wie or_b, pk, etc.

Jedes Fragment entspricht einem bestimmten Teil des Bitcoin-Skripts. Zum Beispiel entspricht pk(key) dem Befehl <key> OP_CHECKSIG und or_b(X,Y) entspricht dem Befehl [X] [Y] BOOLOR.

Miniscript selbst ist eine Ausdruckssprache, die sowohl für Computer als auch für Menschen leicht zu lesen ist.

Für Computer ist das wichtig, damit deine Wallet-Anwendung einen Miniscript-Ausdruck dekodieren und in Empfangsadressen umwandeln kann, so dass Du deine Coins ausgeben kannst.

Für Menschen ist es wichtig, damit man eine Sicherungskopie des Wallet-Deskriptors auf Papier anfertigen und denselben Deskriptor für die Interaktion mit verschiedenen Anwendungen und Tools verwenden kann. Zum Beispiel kannst du deinen Wallet-Deskriptor in ein Tool zur Portfolioverwaltung oder Steuerberichterstattung importieren oder sie in eine mobile Wallet importieren, die nur zur Überwachung dient. Ein Wallet-Deskriptor kann ein Miniscript enthalten und zum Ableiten von Adressen verwendet werden.

Die Miniscript-Ausdruckssprache sieht der Policy-Sprache sehr ähnlich, so dass man die beiden leicht verwechseln kann. Es ist wichtig zu verstehen, dass es sich um zwei völlig unabhängige Sprachen handelt:

Die Policy-Sprache ist ein Werkzeug, das Entwicklern hilft, Ausgabenbedingungen zu erstellen, da sie sehr einfach zu schreiben ist. Sie hat nur eine Handvoll Primitive wie and(X,Y),or(X,Y), older(time) usw., und sie lassen sich alle perfekt miteinander kombinieren. Die Policy-Sprache ist lediglich ein Werkzeug für Entwickler und kein Standard. Es ist nicht garantiert, dass ein Policy-Ausdruck immer denselben Miniscript-Ausdruck ergibt.

Miniscript hingegen ist gut spezifiziert. Nur Miniscript wird verwendet, um Bitcoin Script und gültige Witnesses abzuleiten und Analysen zur Korrektheit durchzuführen. Ein Miniscript-Ausdruck wird immer auf dasselbe Bitcoin Script verweisen.

Warum können Entwickler also nicht direkt Miniscript-Ausdrücke schreiben? Warum sind sie auf einen Policy-Compiler angewiesen? Der Grund dafür ist, dass Miniscript-Ausdrücke nicht einfach von Hand zu schreiben sind, da sie einige der komplexen Eigenschaften von Bitcoin Script übernehmen, weil sie dieses einschließen:

  • Es gibt viel mehr Fragmente in Miniscript als in der Policy-Sprache. So gibt es zum Beispiel mindestens vier verschiedene Möglichkeiten, X or in Miniscript auszudrücken: or_b(X,Y), or_c(X,Y), or_d(X,Y) und or_i(X,Y). Jedes Fragment entspricht einer anderen Art, X or Y in Bitcoin Script auszudrücken, und jedes Fragment hat unterschiedliche Eigenschaften in Bezug auf die Größe des Skripts, die Größe der Witnesses und die Art, wie sie mit anderen Fragmenten zusammengesetzt werden.

  • Es kann viele verschiedene Miniskripte geben, die dieselben Bedingungen kodieren. Der Policy-Compiler kann helfen, z. B. die Skriptgröße zu optimieren, um die Transaktionsgebühren zu senken.

  • Nicht alle Miniscript-Ausdrücke ergeben gültige Skripte - die Fragmente müssen so zusammengesetzt werden, so dass sie den Korrektheitsanforderungen genügen. Der Policy-Compiler sorgt dafür, dass nur gültige Miniscripts erzeugt werden.

Praktische Anwendung: Mapping auf Bitcoin Script

Um zu verstehen, wie ein Miniscript-Ausdruck ein Bitcoin-Skript kodiert, sehen wir uns den folgenden Ausdruck in Aktion an:

or_b(pk(key_1),s:pk(key_2))

Jedes Fragment entspricht einem bestimmten Teil des Bitcoin-Skripts. pk(key) entspricht OP_CHECKSIG, und or_b(X,Y) entsprich [X] [Y] BOOLOR.

Jedes Fragment kann auch mit Wrappern verpackt werden, die durch Buchstaben vor dem Doppelpunkt ":" gekennzeichnet sind. Der Wrapper s:X entspricht OP_SWAP [X]. Wrapper sind im Grunde das Gleiche wie jedes andere Fragment, aber prägnanter. In gewisser Weise sind sie nur syntaktischer Zucker. Du kannst dir z.B. vorstellen, dass s:X dasselbe ist wie s(X) oder dv:older(144) dasselbe ist wie d(v(older(144)).

Der gesamte obige Miniscript-Ausdruck lässt sich also in dieses Bitcoin Script übersetzen:

<key_1> OP_CHECKSIG OP_SWAP <key_2> OP_CHECKSIG OP_BOOLOR
\_________________/   \     \________________/          /
 \       X             \            X       /          /
  \                     \__________________/          /
   \                             Y=s:X               /
    \_______________________________________________/
                         or_b(X,Y)

In diesem speziellen Beispiel würde ein Witnesses zum Ausgeben der Coins die Form <signature2> <signature1> haben, wobei mindestens eine der Signaturen gültig sein muss. OP_SWAP wird benötigt, damit der <key_2> OP_CHECKSIG-Teil nicht auf das oberste Stack-Element angewendet wird, welches das Ergebnis von <signature1> <key_1> OP_CHECKSIG enthält, sondern auf das Element eins unter der Spitze des Stacks, das die Signatur für den zweiten Schlüssel enthält.

Es gibt insgesamt 22 definierte Fragmente und 11 Wrapper, von denen jedes eine konkrete Zuordnung zu Bitcoin Script hat. Eine Liste aller Fragmente und ihrer Zuordnungen findest du in der Spezifikation hier.

Sehen wir uns einen etwas komplizierteren Ausdruck an. Als Beispiel dient die Ausgabenbedingung die wir im ersten Teil dieser Serie verwendet haben:

pubkey1 ODER (pubkey2 in einem Jahr)

Das Skript, das wir zur Kodierung dieser Bedingung verwendet haben, war ziemlich kompliziert und schwer von Hand zu entwerfen:

<pubkey1> OP_CHECKSIG OP_IFDUP OP_NOTIF
  <pubkey2> OP_CHECKSIGVERIFY <52560 (ein Jahr)> OP_CHECKSEQUENCEVERIFY
OP_ENDIF

Mit Miniscript kann diese Ausgabenbedingung jedoch mit der folgenden Policy ausgedrückt werden:

or(10@pk(pubkey1),and(pk(pubkey2),older(52560)))

welche zum Miniscript-Ausdruck kompiliert wird:

or_d(pk(pubkey1),and_v(v:pk(pubkey2),older(52560)))

was wiederum zu dem obigen komplizierten Skript führt, indem die Fragmente wie folgt auf ihre Skript-Gegenstücke abgebildet werden:

  • older(52560) verkörpert <52560> OP_CHECKSEQUENCEVERIFY
  • v:pk(pubkey2) verkörpert OP_CHECKSIGVERIFY
  • and_v(X,Y) verkörpert [X] [Y], also in diesem Fall <pubkey2>OP_CHECKSIGVERIFY <52560> OP_CHECKSEQUENCEVERIFY
  • pk(pubkey1) verkörpert <pubkey1> OP_CHECKSIG
  • or_d(X,Y) verkörpert [X] OP_IFDUP OP_NOTIF [Y] OP_ENDIF, was zu dem obigen Skript führt.

Das 10@ gibt dem Policy-Compiler einen Hinweis darauf, dass wir diesen Ausgabenpfad für viel wahrscheinlicher halten als den anderen. Der Compiler kann diese Information nutzen, um die Gesamtgröße des Skripts für diesen Fall zu optimieren.

Sobald das Bitcoin-Skript bekannt ist, kann eine Wallet-App es in eine Empfangsadresse wie bc1... umwandeln und dich Coins auf dieser Adresse empfangen lassen. Diese Coins werden mit den Ausgabenbedingungen gesperrt, die in der ursprünglichen Policy/Miniscript angegeben sind.

Witnesses generieren

Die entsprechenden Witnesses zum Ausgeben von Coins können auch automatisch aus dem Miniscript-Ausdruck generiert werden, da jedes Fragment definiert, wie gültige Satisfactions und Dissatisfactions für es zu konstruieren sind. Eine Miniscript-basierte Wallet-Anwendung würde diese Methode nutzen, um Transaktionen zu ermöglichen.

Die Liste aller Satisfactions und Dissatisfactions für jedes Fragment findest du unter "Basic satisfactions" hier.

Zum Beispiel kann das Fragment pk(key), das auf <key> OP_CHECKSIG mappt, durch <signature> befriedigt werden. Die endgültige Ausführung des Skripts wäre <signature> <key> OP_CHECKSIG und würde eine 1 auf dem Stack hinterlassen, wenn die Signatur gültig war, und ansonsten eine 0. Das gleiche Fragment kann durch die leere ungültige Signatur <> unzufrieden sein. Eine Dissatisfaction bedeutet, dass das Skript nicht abgebrochen wird und weiterläuft, indem eine 0 auf dem Stack hinterlassen wird.

Wie Witnesses und Skripte kombiniert und ausgeführt werden, damit Coins ausgegeben werden können, erfährst du im ersten Teil dieser Artikelserie.

Erzeugen wir die Witnesses für denselben Ausdruck wie im obigen Beispiel:

or_b(pk(key_1),s:pk(key_2))
  • Das Fragment pk(key kann durch <signatures erfüllt werden, und durch die leere Signatur <> nicht erfüllt werden.
  • Das Fragment or_b(X,Y) Fragment ([X] [Y] OP_BOOL0R) kann durch drei verschiedene Satisfactions befriedigt werden: Entweder sind X und Y befriedigt, oder eine von ihnen: [Satisfaction für Y][Satisfaction für X], oder [Dissatisfaction für Y][Satisfaction für X], oder [Satisfaction für Y][Dissatisfaction für X].
  • Werden die beiden kombiniert: or_b(pk(key_1),s:pk(key_2)) hat diese drei gültigen Witnesses:
    1.<signature2> <signature2>
    2.<> <signature1>
    3.<signature2> <>.

Fazit

In diesem Artikel haben wir uns angesehen, wie Miniscript die Entwicklung komplexer Ausgaben vereinfacht und wie es Wallet-Anwendungen ermöglicht, Coins zu empfangen und auszugeben, die mit solchen Bedingungen gesperrt sind.

In der nächsten Ausgabe werden wir einen vollwertigen Miniscript-Parser und Korrektheitsanalysator in der Programmiersprache Go schreiben. Er wird in der Lage sein, Bitcoin-Empfangsadressen aus beliebigen Miniscript-Ausdrücken zu generieren. Bleib dran!


Du hast noch keine BitBox?

Deine Kryptowährungen sicher zu halten muss nicht schwer sein. Die BitBox02 Hardware Wallet speichert die privaten Schlüssel deiner Kryptowährungen offline. So kannst du deine Coins sicher verwalten.

Die BitBox02 gibt es auch als Bitcoin-only-Version mit einer radikal fokussierten Firmware: weniger Code bedeutet weniger Angriffsfläche, was deine Sicherheit weiter verbessert, wenn du nur Bitcoin speicherst.

Hol dir eine in unserem Shop!‌


Frequently Asked Questions (FAQ)

Was ist Miniscript im Kontext von Bitcoin?
Miniscript führt Fragmente ein, die Teile des Bitcoin-Skripts beinhalten, wodurch die Erstellung komplexer Bitcoin-Skripte durch Zusammensetzen dieser Fragmente vereinfacht wird.

Wie unterscheidet sich Miniscript von der nativen Skriptsprache von Bitcoin?
Während Bitcoin Script für Entwickler aufgrund seiner Komplexität herausfordernd ist, erleichtert Miniscript das Kodieren gewünschter Ausgabebedingungen und das Erstellen gültiger Zeugen, um Bitcoin auszugeben.

Welches sind die zwei unabhängigen Teile von Miniscript?
Miniscript besteht aus der Policy-Sprache, einer High-Level-Ausdruckssprache, und Miniscript selbst, einer Ausdruckssprache, die direkt auf Bitcoin Script abgebildet wird.

Warum wird die Policy-Sprache in Miniscript verwendet?
Die Policy-Sprache hilft Entwicklern, Ausgabenbedingungen einfach zu erstellen. Es ist ein Werkzeug für Entwickler und nicht standardisiert, während Miniscript gut spezifiziert ist und zur Ableitung von Bitcoin Script verwendet wird.

Wie profitieren Wallet-Anwendungen von Miniscript?
Miniscript ermöglicht es Wallet-Anwendungen, seine Ausdrücke zu dekodieren, Empfangsadressen zu generieren und Transaktionen mit Münzen auf der Grundlage der definierten Ausgabebedingungen zu erleichtern.


Shift Crypto ist ein privates Unternehmen mit Sitz in Zürich, Schweiz. Unser Team aus Bitcoin-Entwicklern, Krypto-Experten und Sicherheitsingenieuren entwickelt Produkte, die unseren Kunden eine stressfreie Reise vom Anfänger zum Meister der Kryptowährung ermöglichen. Die BitBox02, unsere Hardware-Wallet der zweiten Generation, ermöglicht es den Nutzern, Bitcoin und andere Kryptowährungen zu speichern, zu schützen und mit Leichtigkeit zu handeln - zusammen mit der dazugehörigen Software, der BitBoxApp.