Zurück zur Startseite

Merkle Tree in Go: Generics und Proof

Der Artikel beschreibt die Implementierung von Merkle Tree in Go mit Generics. Umfasst Knotenschnittstellen, CBOR für Hashing, Generierung des Inklusionsbeweises. Anwendung in Bitcoin, Ethereum, Git.

Merkle Tree in Go von Grund auf bauen: Code und Proof
Advertisement 728x90

Merkle-Baum in Go: Implementierung mit Generics und Inklusionsnachweis

In Systemen mit Millionen von Datensätzen, wie Blockchains, erfordert die Überprüfung der Anwesenheit eines bestimmten Elements normalerweise die Übertragung des gesamten Datensatzes. Der Merkle-Baum löst dieses Problem: Anstelle einer Million Transaktionen werden nur O(log N) Hashes benötigt – etwa 20 für eine Million Elemente. Die Struktur wird als Binärbaum aufgebaut, wobei jeder Knoten seine Kinder hasht und die Wurzel die gesamte Menge zusammenfasst.

Beispiel: Transaktionen A, B, C, D. Das direkte Hashen der gesamten Menge erfordert, dass der Client alle Daten zur Überprüfung hat. Im Baum erhält der Client benachbarte Hashes: H(B), H(CD) – und rekonstruiert unabhängig den Wurzelhash von Blatt A zur Wurzel.

Grundlegende Schnittstellen und Knotentypen

Beginnen Sie mit einer Schnittstelle für alle Baumknoten, die Generics unterstützt:

Google AdInline article slot
type Node[T any] interface {
    String() string
    StringIndent(level int) string
    AddChild(Node[T])
    GetBytes() []byte
    GetChildren() []Node[T]
}

Blätter speichern den Wert und seinen Hash. Für die Serialisierung von Werten beliebigen Typs verwenden Sie CBOR – ein deterministisches Format (RFC 8949), im Gegensatz zu JSON mit instabiler Schlüsselreihenfolge in Maps.

type Leaf[T any] struct {
    Value     T
    ValueHash []byte
}

func NewLeaf[T any](value T, hash hash.Hash) (*Leaf[T], error) {
    hashedValue, err := valueToHash(value, hash)
    if err != nil {
        return nil, err
    }
    return &Leaf[T]{Value: value, ValueHash: hashedValue}, nil
}

func valueToHash(value any, hash hash.Hash) ([]byte, error) {
    encoded, err := cbor.Marshal(value)
    if err != nil {
        return nil, err
    }
    hash.Reset()
    hash.Write(encoded)
    return hash.Sum(nil), nil
}

Binäre Knoten aggregieren linke und rechte Kinder:

type BinaryNode[T any] struct {
    Value []byte
    Right Node[T]
    Left  Node[T]
}

Aufbau des Baums mit einer Hash-Funktionsfabrik

Der Baum wird von einer Struktur mit einer Fabrik zur Erstellung sauberer Hash-Instanzen verwaltet – hash.Hash hat Zustand, also übergeben Sie eine Funktion:

Google AdInline article slot
type BinaryTree[T any] struct {
    newHash func() hash.Hash
}

func NewBinaryTree[T any](newHash func() hash.Hash) *BinaryTree[T] {
    return &BinaryTree[T]{newHash: newHash}
}

// Verwendung
tree := NewBinaryTree[string](newHash)

Konstruktion: Blätter hashen Werte, interne Knoten hashen die Verkettung der Kinderhashes. H(AB) = SHA256(H(A) + H(B)). Die Wurzel ist der finale Hash des gesamten Baums.

Merkle-Nachweis: Erzeugung und Verifizierung

Der Inklusionsnachweis ist ein Schlüsselmechanismus. Der Client kennt den Zielhash (H(A)) und den Wurzelhash. Der Server liefert benachbarte Hashes entlang des Pfads vom Blatt zur Wurzel.

Generierungsalgorithmus

  • Von der Wurzel aus zum Zielblatt absteigen.
  • Auf jeder Ebene den Hash des Geschwisterknotens speichern (links oder rechts, nicht Teil des Pfads).
  • Der Nachweis ist eine Liste dieser Hashes.

Verifizierung von unten nach oben:

Google AdInline article slot
  • Beginnen Sie mit H(A).
  • Sequenziell mit Nachbarn hashen, Seiten wechselnd (links/rechts).
  • Erreichen Sie die Wurzel – eine Übereinstimmung bestätigt die Inklusion.

Für 2^20 Elemente (eine Million) ist der Nachweis 20 Hashes von je 32 Bytes, ~640 Bytes.

Vorteile und Anwendungen

  • Logarithmische Komplexität: O(log N) für den Nachweis, ideal für SPV-Clients.
  • Minimaler Datenverkehr: Bitcoin SPV verifiziert Transaktionen ohne den vollen Block (500+ GB).

Anwendungen:

  • Bitcoin: Transaktionswurzel im Blockheader.
  • Ethereum: State-Trie, Transaktions-Trie, Receipt-Trie.
  • Git: Baumobjekte in Commits.
  • IPFS: Inhaltshash als Wurzel von Chunks.
  • Certificate Transparency: Überwachung von SSL-Zertifikatsprotokollen.

Wichtige Erkenntnisse

  • Merkle-Baum beweist Inklusion in O(log N) ohne Offenlegung anderer Daten.
  • Verwenden Sie Generics in Go für Typsicherheit; CBOR für deterministisches Hashen von beliebigen Werten.
  • Hash-Funktionsfabrik verhindert Race Conditions durch hash.Hash-Zustand.
  • Nachweis wird aus Geschwisterhashes entlang des Pfads vom Blatt zur Wurzel generiert.
  • Effizient für verteilte Systeme: Blockchains, P2P-Speicher.

— Editorial Team

Advertisement 728x90

Weiterlesen