Cómo se forma el árbol de transacciones de Ethereum

Estoy buscando claridad en la estructura del árbol de transacciones. A continuación he enumerado mi entendimiento

  1. Si no me equivoco, los cambios de estado se rastrean en un solo árbol de estado.
  2. Cada bloque tiene un hash de raíz de estado que apunta a la raíz que tiene el estado de las cuentas que participan en el conjunto de transacciones de ese bloque.
  3. Entonces, a medida que los bloques se siguen agregando (es decir, hay cambios en el estado de las cuentas), este árbol de estado crece
  4. Pero, ¿qué hay del árbol de transacciones?
  5. ¿El hash raíz de la transacción está presente en un bloque que apunta a raíces de árboles individuales o, de nuevo, es un árbol único como un árbol de estado (es decir, apunta a varios niveles de raíz)?
  6. Para resaltar esta diferencia, he creado dos diagramas para árboles de estado y de transacción.
  7. Entonces, lo que estoy tratando de decir es que todos los bloques se refieren a un árbol de estado común. Pero cada bloque tiene su propio árbol de transacciones.
  8. Comparta sus puntos de vista y corríjame donde crea que me equivoco.

Árbol de estado ingrese la descripción de la imagen aquí

Árbol de transacciones ingrese la descripción de la imagen aquí

Respuestas (5)

Voy a estar en desacuerdo con la conversación en los comentarios a la otra respuesta. (¡Disculpas de antemano!)

Creo que lo que se describe en la pregunta es generalmente correcto, al menos por la forma en que entendí las cosas.

  1. ¿El hash raíz de la transacción está presente en un bloque que apunta a raíces de árboles individuales o, de nuevo, es un árbol único como un árbol de estado (es decir, apunta a varios niveles de raíz)?

Un árbol individual/raíz de trio. El árbol/trie de estado es diferente porque a menudo estamos actualizando el estado existente de un bloque anterior, mientras que con las transacciones no puede suceder tal cosa.

No creo que el árbol/trie sea en realidad solo una lista (es decir, una sola ramificación hasta el final). En primer lugar, perderíamos las ventajas de usar un árbol/trie. Un árbol/trie nos da O(log x n) recorrido, acceso, etc., mientras que una lista sería O(n) . En segundo lugar, en un árbol de Merkle son las hojas las que contienen los datos reales , con los nodos principales que contienen hashes intermedios hasta la raíz. Por lo tanto, una "lista de Merkle" solo podría contener una transacción, que sería el nodo final o la cola. (Pero entonces la cola sería lo mismo que la raíz... así que tal cosa no tiene sentido de todos modos).

En segundo lugar, del código Geth [1 , 2 , 3] , y creo que del Libro Amarillo [ecuación (176), aunque es difícil decir... ], el orden del árbol de transacciones es 16, es decir, cada El nodo tiene 16 nodos secundarios. De manera similar, la imagen aquí también tiene una etiqueta de "16" para cada uno de los árboles/intentos.

No estoy seguro de cómo se mantiene o traduce el orden de las transacciones en el árbol.

Descargo de responsabilidad: si estoy combinando el orden con el grado al describir la cantidad de niños por nodo en el árbol/trie anterior, entonces disculpas.

¡Gracias por tu aclaración! Esperando en ese caso que el flujo del diagrama sea correcto.
Todos los árboles en ethereum son intentos de patricia (estoy de acuerdo en que esto podría tener un beneficio limitado). La mayoría de los nodos son nodos de rama que tienen 17 hijos (aunque también hay 'nodos de extensión' optimizados). Los TX se ingresan en su trie por el rlp de su txIndex dentro de cada bloque (elegido por el minero)

Absolutamente, la mejor manera de resolver esto que he encontrado es Ethereum Ontology aquí: https://github.com/ConsenSys/EthOn .

No sé si se le permite simplemente vincular a las respuestas, pero si no lo está, debería estarlo. Duplicar este trabajo en StackExchange sería un error.

¡Gracias por su respuesta! Pero a partir de la estructura básica enumerada en mi ejemplo, si pudiera resaltar dónde estoy bien o mal, sería de gran ayuda. Para que al menos pueda obtener la dirección de flujo correcta básica.
Supongo que diría que las transacciones no forman un árbol. Las transacciones forman más una lista. Los bloques forman una lista vinculada básica (vinculada por hashes de bloque), y en cada bloque hay simplemente una lista de transacciones. No hay un árbol real para las transacciones. Creo que esa es tu desconexión básica.
En ese caso, ¿por qué se dice que el bloque Ethereum consta no de una, sino de tres referencias de hash de raíz de árbol de Merkle (es decir, estado, transacción y recibo)?
no sé Una lista puede considerarse un árbol con una rama en cada nodo.
¿Hay alguna secuencia específica en la que se haga referencia a las transacciones en esta lista de enlaces o simplemente en qué orden se seleccionaron?
Cada transacción tiene un ID de transacción que asigna el minero que gana el bloque. (No me cites en esto, pero creo que el minero puede elegir tanto la lista de transacciones incluidas como el pedido, siempre que ese pedido sea válido). Las transacciones se ordenan por ID de transacción dentro del número de bloque.

Casi correcto.

Los árboles Transaction y Receipts son solo Merkle Trees, mientras que State Trie es un Merkle Patricia Trie. Dado que cada bloque de Ethereum contiene una lista de transacciones, y esta lista está "sólida congelada" (Buterin - https://blog.ethereum.org/2015/11/15/merkling-in-ethereum/ ), no serviría de nada hágalo para que obtenga O (log (n)) leído, escrito o eliminado. Lo que importa es que puede dar una prueba de inclusión de Merkle. Además, ni los árboles de transacciones ni de recibos se conservan, se recrean según sea necesario (a diferencia del estado trie, por supuesto).

**Déjame aclarar algo

No necesita un Merkle Patricia Trie para la raíz de txn y la raíz de recibos, pero al menos en pyethereum así es como crean ambos intentos. Mi conjetura es mantener la coherencia, una función para todos los intentos. En común.py

def mk_receipt_sha(receipts): t = trie.Trie(EphemDB()) for i, receipt in enumerate(receipts): t.update(rlp.encode(i), rlp.encode(receipt)) return t.root_hash

¡Gracias por la respuesta! Es bueno saber que estaba en el camino correcto. Así que supongo que desde la perspectiva del diagrama de flujo no es necesario hacer ningún cambio.
@susmit, creo que lo que escribiste está un poco fuera de lugar. stateRoot codifica el estado completo, no solo lo que cambió durante el último bloque. Afortunadamente, la forma en que este árbol está diseñado (patricia trie), solo requiere que se vuelvan a triturar algunas ramas para volver a calcular la nueva raíz.
@ZMitton Le solicita que actualice el diagrama y lo publique aquí
@susmit en realidad, la forma en que lo tiene podría ser correcta, pero para ser claros (y lo que me confundió sobre su dibujo), el estado anterior de AccountA(de 180993) no se incluiría en la construcción de stateRoot para bloque 180994
Todos los árboles en Ethereum son árboles de Patricia por consistencia

Aquí hay un ejemplo de cómo se ve el trie de estado (para transacciones, simplemente reemplace Estado mundial simplificado en la parte superior derecha con Transacciones simplificadas donde son Claves transaction_indexy Valores transaction_content. Es simplificado ya que se omiten algunos detalles, como la codificación RLP)

ingrese la descripción de la imagen aquí

Si no me equivoco, los cambios de estado se rastrean en un solo árbol de estado.

Merkle Patricia Trie es una estructura de datos inmutable. Cada vez que realice cambios en cualquier parte del trie, dará como resultado un nuevo trie con una raíz diferente con un hash diferente. Sin embargo, el nuevo trie podría tener punteros a subárboles del trie de estado anterior. Digamos que tuviste un intento con 3 niveles. Si desea cambiar algo en el nivel más bajo, debe crear un nuevo nodo en el segundo nivel y un nuevo nodo en el primer nivel (la raíz) que se registrará en la cadena de bloques.

Cada bloque tiene un hash de raíz de estado que apunta a la raíz que tiene el estado de las cuentas que participan en el conjunto de transacciones de ese bloque.

Cada bloque tiene el hash de la raíz del estado que apunta a la raíz del estado y tiene el estado de todas las cuentas, no solo las que participan en el conjunto de transacciones de ese bloque. Como mencioné anteriormente, cada vez que se cambia el estado de cualquier cuenta, se cambia la raíz de todo el árbol.

¿El hash raíz de la transacción está presente en un bloque que apunta a raíces de árboles individuales o, de nuevo, es un árbol único como un árbol de estado (es decir, apunta a varios niveles de raíz)?

Cada bloque tiene su propia prueba de transacción donde las claves son índices de transacción codificados en RLP dentro del bloque y los valores son transacciones codificadas en RLP.

Para resaltar esta diferencia, he creado dos diagramas para árboles de estado y de transacción.

En su primera imagen donde se representa el estado trie, apunta la raíz del estado del bloque 180994 a la raíz del estado anterior trie como su hijo izquierdo. Esto normalmente no debería suceder. Creo que lo que quisiste decir es que la nueva raíz debería apuntar al hijo izquierdo de la raíz anterior.

Creo que tu diagrama es correcto. Encontrar una transacción debería ser tan fácil como hacer una búsqueda binaria en los bloques. Considere la función:

find block where: someBlockT1 <= timestamp(tx) <= someBlockT2

Luego busque a través del Tx merkle trie que está contenido en el bloque devuelto.