Tauchen Sie ein in EVM-Datenstrukturen, Transaktionsbelege und Ereignisprotokolle

ForesightNews

Das Verständnis der Datenstrukturen, aus denen eine Blockchain besteht, hilft uns, kreative Möglichkeiten zur Analyse dieser Daten zu finden.

**Geschrieben von:**NOXX

Kompilieren: Flush

Das Navigieren in On-Chain-Daten ist eine wesentliche Fähigkeit für jeden, der den Web3-Bereich verstehen möchte. Das Verständnis der Datenstrukturen, aus denen eine Blockchain besteht, hilft uns, über kreative Möglichkeiten zur Analyse dieser Daten nachzudenken. Gleichzeitig machen diese On-Chain-Daten einen großen Teil der verfügbaren Daten aus. Dieser Beitrag befasst sich mit einer wichtigen Datenstruktur im EVM, dem Transaktionsbeleg und dem zugehörigen Ereignisprotokoll.

Warum loggen

Bevor wir beginnen, sprechen wir kurz darüber, warum wir als Soliditätsentwickler Ereignisprotokolle verwenden müssen:

  • Das Ereignisprotokoll ist eine kostengünstigere Option für die Datenspeicherung, auf die nicht durch den Vertrag zugegriffen werden muss, und kann auch den gespeicherten Zustand rekonstruieren, indem bestimmte Variablen im Smart Contract getestet und Variablen indiziert werden.
  • Die Ereignisprotokollierung ist eine Möglichkeit, eine Web3-Anwendung auszulösen, die auf ein bestimmtes Ereignisprotokoll lauscht.

EVM-Knoten müssen Protokolle nicht für immer aufbewahren und können durch das Löschen alter Protokolle Platz sparen. Verträge haben keinen Zugriff auf den Protokollspeicher, daher benötigen Knoten sie nicht, um Verträge auszuführen. Die Vertragsspeicherung hingegen ist für die Vertragsabwicklung erforderlich und kann daher nicht gelöscht werden.

Ethereum Block Merkle Root

In Teil 4 haben wir uns intensiv mit dem Ethereum-Framework befasst, insbesondere mit dem State-Merkle-Wurzelteil. Die State Root ist eine von drei Merkle Roots, die im Blockheader enthalten sind. Die anderen beiden sind Transaction Root und Receipt Root.

Als Input für den Aufbau dieses Frameworks verweisen wir auf Block 15001871 auf Ethereum, der 5 Transaktionen mit den zugehörigen Belegen und gesendeten Ereignisprotokollen enthält.

Blockheader

Wir beginnen mit drei Teilen im Block-Header: Transaction Root, Receipt Root und Logs Bloom (eine kurze Einführung in den Block-Header finden Sie in Teil 4).

Quelle:

Im Ethereum-Client unter Transaction Root und Receipt Root enthält Merkle Patricia Tries alle Transaktionsdaten und Quittungsdaten im Block. Dieser Artikel konzentriert sich nur auf alle Transaktionen und Belege, auf die ein Knoten zugreifen kann.

Die über den Ethereum-Knoten gefundenen Blockheader-Informationen des Blocks 15001871 lauten wie folgt:

Der LogsBloom im Blockheader ist eine Schlüsseldatenstruktur, die später in diesem Artikel erwähnt wird. Beginnen wir zunächst mit den Daten, die sich unter dem Transaktionsstamm, dem Transaktionsversuch, befinden.

Transaktionsbaum-Transaktionsversuch

Transaction Trie ist ein Datensatz, der TransactionRoot generiert und Transaktionsanforderungsvektoren aufzeichnet. Transaktionsanforderungsvektoren sind Informationen, die zum Ausführen einer Transaktion erforderlich sind. Die in einer Transaktion enthaltenen Datenfelder sind wie folgt:

  • Typ – Transaktionstyp (traditionelle LegacyTxType-Transaktion, AccessListTxType EIP-2930-Einführung, DynamicFeeTxType EIP-1559-Einführung)
  • ChainId – EIP155-Ketten-ID der Transaktion
  • Daten – Eingabedaten der Transaktion
  • AccessList – Zugriffsliste für Transaktionen
  • Gas – das Gaslimit der Transaktion
  • GasPrice – der Gaspreis der Transaktion
  • GasTipCap – die Anreizprämie für Bergleute, deren Transaktionseinheit Gas die Grundgebühr für das erste Paket übersteigt, maxPriorityFeePerGas in Geth wird durch EIP1559 definiert
  • GasFeeCap – die Obergrenze der Gasgebühr pro Transaktionseinheit, maxFeePerGas in Geth (GasFeeCap ≥ baseFee + GasTipCap)
  • Wert – die gehandelte Menge an Ethereum
  • Nonce – die Nonce des Erstellers des Handelskontos
  • An – Die Empfängeradresse der Transaktion. Bei Vertragserstellungstransaktionen gibt To einen Nullwert zurück
  • RawSignaturues – Signaturwerte V, R, S von Transaktionsdaten

Nachdem wir die obigen Datenfelder verstanden haben, werfen wir einen Blick auf die erste Transaktion von Block 15001871

Durch die Ethclient-Abfrage von Geth können Sie sehen, dass sowohl ChainId als auch AccessList „omitempty“ haben. Wenn das Feld leer ist, wird es in der Antwort weggelassen, um die Größe der serialisierten Daten zu verringern oder zu verkürzen.

Codequelle:

Diese Transaktion stellt die Übertragung von USDT-Tokens an die Adresse 0xec23e787ea25230f74a3da0f515825c1d820f47a dar. Die An-Adresse ist die ERC20 USDT-Vertragsadresse 0xdac17f958d2ee523a2206206994597c13d831ec7. Durch EINGABEDATEN können wir sehen, dass die Funktionssignatur 0xa9059cbb der Funktion Übertragung (Adresse, UINT256) entspricht und 42,251 USDT (Genauigkeit von 6) an 0x2b279b8 (45251000) an 0xEC23E787EA25230F an 0xEC23E787EA25230F übertragen wird. 74A3 DA0F515825C1D820F47A Adresse.

Möglicherweise ist Ihnen aufgefallen, dass diese Transaktionsdatenstruktur nichts über das Ergebnis der Transaktion aussagt. War die Transaktion also erfolgreich? Wie viel Gas verbraucht es? Welche Ereignisdatensätze werden ausgelöst? An dieser Stelle stellen wir den Receipt Trie vor.

Quittungsversuch

So wie ein Einkaufsbeleg das Ergebnis einer Transaktion aufzeichnet, macht ein Objekt im Quittungsversuch dasselbe für eine Ethereum-Transaktion, zeichnet aber auch einige zusätzliche Details auf. Um auf die oben gestellte Frage zu Transaktionsbelegen zurückzukommen, konzentrieren wir uns auf die Protokolle, die die folgenden Ereignisse ausgelöst haben.

Fragen Sie die On-Chain-Daten von 0x311b erneut ab und erhalten Sie den Transaktionsbeleg. Zu diesem Zeitpunkt werden die folgenden Felder abgerufen:

Codequelle:

  • Typ – Transaktionstyp (LegacyTxType, AccessListTxType, DynamicFeeTxType)
  • PostState(root) – StateRoot, der Wurzelknoten des nach der Ausführung der Transaktion generierten Statusbaums. Der entsprechende Wert in der Abbildung ist 0x, wahrscheinlich aufgrund von EIP98
  • CumulativeGasUsed – Kumulierter Gesamtgasverbrauch dieser Transaktion und aller vorherigen Transaktionen im selben Block
  • Bloom(logsBloom) – Bloom-Filter für Ereignisprotokolle, der für die effiziente Suche und den Zugriff auf Vertragsereignisprotokolle in der Blockchain verwendet wird und es Knoten ermöglicht, schnell abzurufen, ob ein bestimmtes Ereignis in einem Block aufgetreten ist, ohne den Block vollständig zu analysieren. Alle Transaktionsbelege im Block
  • Protokolle – ein Array von Protokollobjekten mit Protokolleinträgen, die durch Vertragsereignisse generiert wurden, die während der Transaktionsausführung ausgelöst wurden
  • TxHash – der mit der Quittung verknüpfte Transaktions-Hash
  • ContractAddress – Wenn die Transaktion einen Vertrag erstellen soll, die Adresse, an der der Vertrag bereitgestellt wurde. Handelt es sich bei der Transaktion nicht um eine Vertragserstellung, sondern beispielsweise um eine Übertragung oder Interaktion mit einem bereitgestellten Smart Contract, ist das Feld „ContractAddress“ leer
  • GasUsed – das durch diese Transaktion verbrauchte Gas
  • BlockNumber – die Blocknummer des Blocks, in dem diese Transaktion stattgefunden hat
  • TransactionIndex – Der Transaktionsindex innerhalb des Blocks. Der Index bestimmt, welche Transaktion zuerst ausgeführt wird. Diese Transaktion befindet sich am Anfang des Blocks, also Index 0

Nachdem wir nun die Zusammensetzung des Transaktionsbelegs kennen, werfen wir einen genaueren Blick auf die LogsBloom- und Log-Array-Protokolle im Transaktionsbeleg.

Ereignisprotokolle

Durch den USDT-Vertragscode im Ethereum-Mainnet können wir sehen, dass das Übertragungsereignis in Zeile 86 des Vertrags deklariert wird und die beiden Eingabeparameter das Schlüsselwort „indexed“ haben.

(Codequelle:

Wenn eine Ereigniseingabe „indiziert“ ist, können wir Protokolle über diese Eingabe schnell finden. Wenn Sie beispielsweise den Index „von“ oben verwenden, ist es möglich, alle Ereignisprotokolle vom Typ „Übertragung“ mit der „Von“-Adresse 0x5041ed759dd4afc3a72b8192c143f72f4724081a zwischen den Blöcken X und Y abzurufen. Wir können auch sehen, dass das Ereignisprotokoll ausgelöst wird, wenn die Übertragungsfunktion in Zeile 138 aufgerufen wird. Es ist erwähnenswert, dass der aktuelle Vertrag eine frühere Version von Solidity verwendet, sodass das Schlüsselwort emit fehlt.

Gehen Sie zurück zu den erhaltenen On-Chain-Daten:

Codequelle:

Lassen Sie uns etwas tiefer in die Adress-, Themen- und Datenfelder eintauchen.

Thementhemen

„Themen“ ist ein Indexwert. Aus der obigen Abbildung können wir ersehen, dass es in den Abfragedaten in der Kette drei Indexparameter von Themen gibt, während das Übertragungsereignis nur zwei Indexparameter (von und nach) hat. Dies liegt daran, dass das erste Thema immer der Funktionssignatur-Hash des Ereignisses ist. Die Signatur der Ereignisfunktion im aktuellen Beispiel lautet Transfer(address, address, uint256). Durch Hashing mit keccak256 erhalten wir das Ergebnis ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef.

(Online-Tool:

Wenn wir wie oben erwähnt das Feld „Von“ abfragen, aber gleichzeitig den Typ des Abfrage-Ereignisprotokolls nur auf Ereignisprotokolle vom Typ „Übertragung“ beschränken möchten, müssen wir nach Ereignistyp filtern, indem wir Ereignissignaturen indizieren.

Wir können bis zu 4 Themen haben, jedes Thema hat eine Größe von 32 Bytes (wenn der Typ des Indexparameters größer als 32 Bytes ist (d. h. String und Bytes), werden nicht die eigentlichen Daten gespeichert, sondern ein keccak256-Digest der Daten gespeichert wird). Wir können 3 Indexparameter deklarieren, da der erste Parameter von der Ereignissignatur übernommen wird. Es gibt jedoch eine Situation, in der das erste Thema keine Hash-Ereignissignatur ist. Dies ist bei der Deklaration anonymer Ereignisse der Fall. Dies eröffnet die Möglichkeit, 4 Indexierungsparameter anstelle der vorherigen 3 zu verwenden, verliert jedoch die Möglichkeit, Ereignisnamen zu indizieren. Ein weiterer Vorteil anonymer Ereignisse besteht darin, dass ihre Bereitstellung kostengünstiger ist, da sie kein zusätzliches Thema erzwingen. Die anderen Themen sind die Werte der Indizes „von“ und „bis“ aus dem Transferereignis.

DatenDaten

Der Datenabschnitt enthält die restlichen (nicht indizierten) Parameter aus dem Ereignisprotokoll. Im obigen Beispiel gibt es einen Wert 0x0000000000000000000000000000000000000000000000000000002b279b8, der 45251000 in Dezimalzahl ist, was dem oben genannten Betrag von 45,251 $ entspricht. Wenn es mehrere solcher Parameter gibt, werden diese an das Datenelement angehängt. Das folgende Beispiel zeigt den Fall von mehr als einem nicht indizierten Parameter.

Das aktuelle Beispiel fügt dem Transfer-Ereignis ein zusätzliches „Steuer“-Feld hinzu. Angenommen, die eingestellte Steuer beträgt 20 %, dann sollte der Steuerwert 45251000 * 20 % = 9050200 sein, sein Hexadezimalwert ist 0x8a1858, da der Typ dieser Zahl uint256 ist und der Datentyp 32 Byte beträgt, müssen Sie The Der Hexadezimalwert wird mit 32 Bytes gefüllt und das Ergebnis des Datenelements ist 0x0000000000000000000000000000000000000000000000000000000002b279b8000000000000000000000000000000000 0000 00000000000000000000008a1858.

Adresse

Das Adressfeld ist die Adresse des Vertrags, der das Ereignis ausgelöst hat. Ein wichtiger Hinweis zu diesem Feld ist, dass es indiziert wird, auch wenn es nicht im Themenbereich enthalten ist. Der Grund dafür ist, dass das Übertragungsereignis Teil des ERC20-Standards ist. Dies bedeutet, dass Übertragungsereignisse aus allen ERC20-Verträgen abgerufen werden, wenn die Protokolle von ERC20-Übertragungsereignissen gefiltert werden müssen. Und durch die Indizierung der Vertragsadresse kann die Suche auf einen bestimmten Vertrag/Token eingegrenzt werden, wie im Beispiel USDT.

Opcodes Opcodes

Schließlich gibt es noch den LOG-Opcode. Sie reichen von LOG0, wenn keine Themen vorhanden sind, bis LOG4, wenn 4 Themen vorhanden sind. LOG3 ist das, was wir in unserem Beispiel verwenden. Enthält Folgendes:

  • offset – Speicheroffset, der die Startposition der Datenfeldeingabe angibt
  • Länge – Länge der aus dem Speicher zu lesenden Daten
  • Thema x(0 - 4) – der Wert von Thema x

(Quelle:

Offset und Länge definieren, wo sich die Daten im Datenabschnitt im Speicher befinden.

Nachdem wir die Struktur des Protokolls und die Indexierung eines Themas verstanden haben, wollen wir nun verstehen, wie Indexelemente durchsucht werden.

Blütenfilter Blütenfilter

Das Geheimnis für eine schnellere Indizierung von Elementen, die durchsucht werden, ist der Bloom-Filter.

Der Llimllib-Artikel enthält eine gute Definition und Erklärung dieser Datenstruktur.

„Der Bloom-Filter ist eine Datenstruktur, die verwendet werden kann, um zu bestimmen, ob sich ein Element in einer Sammlung befindet. Er zeichnet sich durch einen schnellen Betrieb und einen geringen Speicherbedarf aus. Die Kosten für effizientes Einfügen und Abfragen bestehen darin, dass Bloom-Filter wahrscheinlichkeitsbasierte Daten sind.“ Struktur: Es kann uns nur sagen, dass ein Element definitiv nicht in der Menge oder möglicherweise in der Menge ist. Die zugrunde liegende Datenstruktur eines Bloom-Filters ist ein Bitvektor.“

Unten finden Sie ein Beispiel für einen Bitvektor. Weiße Zellen stellen Bits mit dem Wert 0 dar, grüne Zellen stellen Bits mit dem Wert 1 dar.

Diese Bits werden durch Eingabe und Hashing auf 1 gesetzt. Der resultierende Hash-Wert wird als Bitindex verwendet, auf dem das Bit aktualisiert werden soll. Der obige Bitvektor ist das Ergebnis der Anwendung zweier verschiedener Hashes auf den Wert „ethereum“, um einen 2-Bit-Index zu erhalten. Der Hash stellt eine Hexadezimalzahl dar. Um den Index zu erhalten, nehmen Sie die Zahl und wandeln sie in einen Wert zwischen 0 und 14 um. Es gibt viele Möglichkeiten, dies zu tun, wie zum Beispiel Mod 14.

Rezension

Mit einem Bloom-Filter für Transaktionen, also einem Bitvektor, kann dieser in Ethereum gehasht werden, um zu bestimmen, welche Bits im Bitvektor aktualisiert werden sollen. Die Eingabe ist das Adressfeld und das Thema des Ereignisprotokolls. Sehen wir uns die LogsBloom im Transaktionsbeleg an, bei denen es sich um einen transaktionsspezifischen Bloom-Filter handelt. Eine Transaktion kann mehrere Protokolle haben, die die Adresse/das Thema aller Protokolle enthalten.

Wenn Sie zum Blockheader zurückblicken, finden Sie einen weiteren LogsBloom. Dies ist ein Bloom-Filter für alle Transaktionen innerhalb des Blocks. Enthält alle Adressen/Themen in jedem Protokoll für jede Transaktion.

Diese Bloom-Filter werden hexadezimal und nicht binär ausgedrückt. Sie sind 256 Byte lang und stellen einen 2048-Bit-Vektor dar. Wenn wir uns auf das Llimllib-Beispiel oben beziehen, beträgt unsere Bitvektorlänge 15 und die Bitindizes 2 und 13 werden auf 1 umgedreht. Mal sehen, was wir bekommen, wenn wir es in Hex umwandeln.

Obwohl die hexadezimale Darstellung nicht wie ein Bitvektor aussieht, ist dies in logsBloom der Fall.

Abfrageanfragen

Es wurde bereits eine Abfrage erwähnt: „Finden Sie alle Ereignisprotokolle des Übertragungstyps, deren „Von“-Adresse 0x5041ed759dd4afc3a72b8192c143f72f4724081a zwischen den Blöcken X und Y lautet.“ Wir können das Ereignissignaturthema abrufen, das ein Thema vom Typ Transfer und vom Wert (0x5041…) darstellt, und bestimmen, welche Bitindizes im Bloom-Filter auf 1 gesetzt werden sollen.

Wenn Sie logsBloom im Blockheader verwenden, können Sie überprüfen, ob eines dieser Bits nicht auf 1 gesetzt ist. Wenn nicht, kann festgestellt werden, dass im Block keine Protokolle vorhanden sind, die der Bedingung entsprechen. Und wenn sich herausstellt, dass diese Bits gesetzt sind, wissen wir, dass sich wahrscheinlich ein passendes Protokoll im Block befindet. Aber nicht ganz sicher, da der Blockheader logsBloom aus mehreren Adressen und Themen besteht. In anderen Ereignisprotokollen sind möglicherweise Übereinstimmungsbits gesetzt. Aus diesem Grund ist ein Bloom-Filter eine probabilistische Datenstruktur. Je größer der Bitvektor, desto unwahrscheinlicher ist es, dass es zu Bitindexkollisionen mit anderen Protokollen kommt. Sobald Sie über einen passenden Bloom-Filter verfügen, können Sie mit derselben Methode logsBloom nach einzelnen Belegen abfragen. Wenn eine Übereinstimmung erzielt wird, kann der tatsächliche Protokolleintrag angezeigt werden, um das Objekt abzurufen.

Führen Sie die oben genannten Vorgänge für die Blöcke X bis Y aus, um schnell alle Protokolle zu finden und abzurufen, die die Kriterien erfüllen. So funktioniert der Bloom-Filter konzeptionell.

Schauen wir uns nun die in Ethereum verwendete Implementierung an.

Geth-Implementierung – Bloom-Filter

Nachdem wir nun wissen, wie der Bloom-Filter funktioniert, lernen wir, wie der Bloom-Filter das Screening Schritt für Schritt von Adresse/Thema bis hin zu logsBloom in einem tatsächlichen Block durchführt.

Zunächst einmal aus der Definition des Ethereum Yellow Paper:

Quelle:

„Wir definieren eine Bloom-Filterfunktion M, die Protokolleinträge in einen einzigen 256-Byte-Hash reduziert:

In ist ein spezialisierter Bloom-Filter, der bei einer beliebigen Bytefolge drei Bits im Jahr 2048 setzt. Dies geschieht, indem die unteren 11 Bits jedes der ersten drei Bytepaare im Keccak-256-Hash der Bytesequenz verwendet werden. "

Nachfolgend finden Sie ein Beispiel und einen Verweis auf eine Geth-Client-Implementierung, um das Verständnis der obigen Definitionen zu erleichtern.

Hier ist das Transaktionsprotokoll, das wir uns auf Etherscan angesehen haben.

Das erste Thema ist die Ereignissignatur 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef und wandelt diesen Wert in den Bitindex um, der aktualisiert werden soll.

Unten ist die Funktion „bloomValues“ aus der Geth-Codebasis.

Diese Funktion empfängt das Ereignissignaturthema, wie zum Beispiel: 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef und andere Daten, und gibt den Bitindex zurück, der im Bloom-Filter aktualisiert werden muss.

Codequelle:

  1. Die Funktion „bloomValues“ empfängt als Eingabe ein Thema (im Beispiel die Ereignissignatur) und einen Hashbuf (ein leeres Byte-Array der Länge 6).
  1. Siehe den Yellow-Paper-Ausschnitt „Die ersten drei Bytepaare in einem Keccak-256-Hash einer Bytefolge“. Diese drei Bytepaare sind 6 Bytes, was der Länge von Hashbuf entspricht.

  2. Beispieldaten: 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef.

  1. Der Befehl sha zwischen den Zeilen 140 – 144 hasht die Eingabedaten und lädt die Ausgabe in hashbuf.
  1. Das hexadezimale Ergebnis der SHA-Ausgabe mit keccak256 ist (bei Verwendung von keccak 256 als Funktionssignatur ist die Eingabe vom Typ Text, hier jedoch vom hexadezimalen Typ): ada389e1fc24a8587c776340efb91b36e675792ab631816100d55df0b5cf3cbc.

  1. Der aktuelle Inhalt von hasbuf [ad, a3, 89, e1, fc, 24] (hexadezimal). Jedes Hexadezimalzeichen repräsentiert 4 Bits.

3 Berechnen Sie v1.

1)Hashbuf [1] = 0xa3 = 10100011 für bitweises UND mit 0x7. 0x7 = 00000111.

  1. Ein Byte besteht aus 8 Bits. Wenn Sie einen Bitindex erhalten möchten, müssen Sie sicherstellen, dass der erhaltene Wert zwischen 0 und 7 des Null-Index-Arrays liegt. Verwenden Sie bitweises UND für Hashbuf [1] Beschränkt auf Werte zwischen 0 und 7. Im Beispiel berechnet: 10100011 & 00000111 = 00000011 = 3.

  2. Dieser Bitindexwert wird mit einem Bitverschiebungsoperator verwendet, dh um 3 Bits nach links verschoben, was zu einem 8-Bit-Byteindex 00001000 führt, um ein umgedrehtes Bit zu erzeugen.

  3. v1 ist das gesamte Byte und nicht der eigentliche Bitindex, da dieser Wert später im Bloom-Filter bitweise ODER-verknüpft wird. Die ODER-Operation stellt sicher, dass alle entsprechenden Bits im Bloom-Filter ebenfalls umgedreht werden.

  1. Wir haben jetzt Byte-Werte, benötigen aber weiterhin Byte-Indizes. Der Bloom-Filter ist 256 Bytes lang (2048 Bits), daher müssen wir wissen, auf welchem Byte das bitweise ODER ausgeführt werden soll. Der Wert i1 repräsentiert diesen Byte-Index.
  1. Setzen Sie den Hashbuf in die Big-Endian-uint16-Bytereihenfolge, wodurch die ersten 2 Bytes des Bitarrays begrenzt werden, was im Beispiel 0xada3 = 1010110110100011 ist.

  2. Bitweise UND diesen Wert mit 0x7ff = 0000011111111111. Es gibt 11 Bits, bei denen 0x7ff auf 1 gesetzt ist. Wie im gelben Papier erwähnt, „erreicht es dazu die unteren 11 Bits jedes der ersten drei Paare“. Dies ergibt den Wert 0000010110100011, der 1010110110100011 und 0000011111111111 ist.

  3. Anschließend den Wert um 3 Bit nach rechts verschieben. Dadurch wird eine 11-stellige Zahl in eine 8-stellige Zahl umgewandelt. Wir möchten einen Byte-Index und die Byte-Länge des Bloom-Filters beträgt 256, daher muss der Byte-Indexwert in diesem Bereich liegen. Und eine 8-Bit-Zahl kann einen beliebigen Wert zwischen 0 und 255 haben. In unserem Beispiel ist dieser Wert 0000010110100011, um 3 Bits nach rechts verschoben 10110100 = 180.

  4. Berechnen Sie unseren Byte-Index anhand von BloomByteLength, wobei Sie wissen, dass er 256 minus den berechneten 180 minus 1 beträgt. Subtrahieren Sie 1, um das Ergebnis zwischen 0 und 255 zu halten. Dies gibt uns den zu aktualisierenden Byte-Index, der sich in diesem Fall als Byte 75 herausstellt, und so haben wir i1 berechnet.

  1. Aktualisieren Sie den Bitindex 3 im 75. Byte des Bloom-Filters (0 ist der Index, also das 4. Bit), was durch Ausführen einer bitweisen ODER-Operation von v1 am 75. Byte im Bloom-Filter erfolgen kann.
  1. Wir haben nur das erste Bytepaar 0xada3 behandelt, was für die Bytepaare 2 und 3 erneut durchgeführt wurde. Jede Adresse/jedes Thema aktualisiert 3 Bits in einem 2048-Bit-Vektor. Wie im Yellow Paper erwähnt, „setzt der Bloom-Filter im Jahr 2048 drei Bits bei einer beliebigen Bytefolge.“

  2. Der Status von Bytepaar 2 aktualisiert den Bitindex 1 in Byte 195 (gemäß den Verfahren 3 und 4 ausführen, das Ergebnis ist in der Abbildung dargestellt).

  3. Bytepaar 3 Statusaktualisierungsbitindex 4 in Byte 123.

  4. Wenn das zu aktualisierende Bit bereits von einem anderen Thema umgedreht wurde, bleibt es so, wie es ist. Wenn nicht, wird auf 1 umgeschaltet.

Durch den obigen Vorgang kann festgestellt werden, dass das Ereignissignaturthema die folgenden Bits im Bloom-Filter umdreht:

  • Bitindex 3 in Byte 75
  • Bitindex 1 in Byte 195
  • Bitindex 4 in Byte 123

Ein Blick auf die logBlooms im Transaktionsbeleg, konvertiert in Binärwerte, verifiziert, dass diese Bitindizes gesetzt sind.

In der Zwischenzeit können Leser, die mehr über die Implementierung der Protokollsuche und des Bloom-Filters erfahren möchten, den BloomBits Trie-Artikel lesen.

An diesem Punkt ist unsere ausführliche Diskussion der EVM-Artikelserie zu Ende und wir werden Ihnen in Zukunft weitere hochwertige technische Artikel präsentieren.

Original anzeigen
Disclaimer: The information on this page may come from third parties and does not represent the views or opinions of Gate. The content displayed on this page is for reference only and does not constitute any financial, investment, or legal advice. Gate does not guarantee the accuracy or completeness of the information and shall not be liable for any losses arising from the use of this information. Virtual asset investments carry high risks and are subject to significant price volatility. You may lose all of your invested principal. Please fully understand the relevant risks and make prudent decisions based on your own financial situation and risk tolerance. For details, please refer to Disclaimer.
Kommentieren
0/400
GateUser-17100469vip
· 2025-07-09 02:40
Starten Sie mit Kraft 🚀
Original anzeigenAntworten0