Merklización de SVM en SOON

Avanzado1/27/2025, 12:53:10 AM
SOON Network introduce una estructura de árbol de Merkle dentro de la Máquina Virtual Solana (SVM) para abordar el problema de la falta de raíces de estado globales. Esta mejora fortalece las capacidades de SVM en verificación de integridad, pruebas de fraude y operaciones entre capas dentro del sistema de agregación. Al incrustar la raíz del estado directamente en la cadena de bloques SVM, SOON mejora la seguridad y la escalabilidad, proporcionando un soporte más sólido para la agregación SVM.

La Máquina Virtual Solana (SVM) está siendo ampliamente adoptada como la capa de ejecución para varias soluciones de Capa-2 (L2). Sin embargo, una limitación clave en el diseño original de SVM es la oscuridad de su raíz de estado global. Esto plantea desafíos significativos para los sistemas de rollup, donde las raíces de estado global son críticas para garantizar la integridad, permitir pruebas de fraude y respaldar operaciones entre capas.

En un rollup, el proponente envía periódicamente la raíz del estado L2 (raíz de Merkle) a la Capa-1 (L1), estableciendo puntos de control para la cadena L2. Estos puntos de control permiten pruebas de inclusión para cualquier estado de cuenta, lo que permite una ejecución sin estado de un punto de control a otro. Las pruebas de fraude dependen de este mecanismo, ya que los participantes pueden proporcionar pruebas de inclusión para verificar entradas válidas durante disputas. Además, los árboles de Merkle mejoran la seguridad de los puentes canónicos al permitir a los usuarios generar pruebas de inclusión para transacciones de retiro, asegurando interacciones sin confianza entre L2 y L1.

Para abordar estos desafíos, Red SOONintroduce árboles de Merkle en la capa de ejecución de la SVM, lo que permite a los clientes proporcionar pruebas de inclusión. SOON se integra con Prueba-de-Historia utilizando entradas únicas para incrustar raíces de estado directamente en blockchains basados en SVM. Con esta innovación, la pila de SOON puede admitir nuevos rollups basados en SVM con seguridad, escalabilidad y utilidad mejoradas.

El Problema de Merklización de Solana

Solana siempre ha sido diseñada con alta capacidad de procesamiento como objetivo principal, lo que requiere intercambios de diseño deliberados —especialmente durante su desarrollo inicial— para lograr su rendimiento novedoso. Entre estos intercambios, una de las decisiones más impactantes se centró en cómo y cuándo Solana merkleizaría su estado.

Finalmente, esta decisión creó desafíos significativos para los clientes al demostrar el estado global, así como la inclusión de transacciones y la verificación simple de pagos (SPV). La falta de una raíz de estado hash consistentemente representativa del estado SVM merklizado plantea dificultades considerable para proyectos como clientes ligeros y rollups.

Echemos un vistazo a cómo se realiza la merklización en otras cadenas y luego identifiquemos los desafíos presentados por la arquitectura del protocolo de Solana.

Árboles de Merkle en Bitcoin

En Bitcoin, las transacciones se almacenan en un bloque mediante un árbol de Merkle, cuya raíz se almacena en el cabecera del bloque. El protocolo Bitcoin hasheará las entradas y salidas de una transacción (así como algunos otros metadatos) en un ID de transacción(TxID). Para demostrar el estado en Bitcoin, un usuario simplemente puede calcular una prueba de Merkle para verificar el TxID contra la raíz de Merkle del bloque.

Este proceso de verificación también valida el estado, ya que el ID de Tx es único para algún conjunto de entradas y salidas, ambas reflejando cambios en el estado de la dirección. Tenga en cuenta que las transacciones de Bitcoin también pueden contener Scripts de Taproot, que producen salidas de transacción que pueden ser verificadas durante la verificación, a menudo volviendo a ejecutar el script utilizando las entradas de la transacción y los datos de testigos del script, y validando contra sus salidas.

Árboles de Merkle en Ethereum

Similar to Bitcoin, Ethereum stores transactions using a custom data structure (derived from a Merkle tree) called a Trie de Merkle Patricia(MPT). Esta estructura de datos está diseñada para actualizaciones rápidas y optimización en conjuntos de datos grandes. Naturalmente, esto se debe a que Ethereum tiene significativamente más entradas y salidas que gestionar que Bitcoin.

La Máquina Virtual Ethereum(EVM) actúa como una máquina de estado global. El EVM es esencialmente un entorno de computación distribuido gigantesco que admite ejecutable contratos inteligentes, cada uno de los cuales reserva su propio espacio de direcciones en la memoria global. Como resultado, los clientes que desean verificar el estado en Ethereum deben tener en cuenta no solo el resultado de una transacción (logs, código de retorno, etc.) sino también los cambios en el estado global como resultado de la transacción.

Afortunadamente, la EVM hace un uso inteligente de tres estructuras de trie importantes, que almacenan sus raíces en cada encabezado de bloque.

  • trie de estado: Un almacén gigante de pares clave-valor de todos los estados en Ethereum, incluida cada dirección de Ethereum y los datos almacenados dentro de cada cuenta. Para contratos inteligentes, estas cuentas almacenan otro trie llamado una trie de almacenamiento, que es otro mapa clave-valor de todos los datos del contrato inteligente para su espacio de direcciones.
  • Árbol de transacciones: Almacén de valores clave de todas las transacciones en un bloque, donde la clave es la ID de la transacción y el valor es los datos de la transacción.
  • trie de recibos: Trie que contiene los recibos (estado, eventos) de cada transacción en el bloque, hasheados por el índice de cada transacción en el bloque. Cada recibo contiene información sobre la ejecución de la transacción, incluido un hash del estado posterior a la transacción del árbol de estados.

Dada una transacción, un cliente puede demostrar su inclusión en un bloque evaluando la raíz del árbol de transacciones (como Bitcoin), su resultado evaluando el árbol de recibos y los cambios en el estado global evaluando el árbol de estado.

Árboles de Merkle en Solana

Una de las razones por las que Solana tiene una alta capacidad de procesamiento es el hecho de que no tiene la estructura de árbol múltiple que tiene Ethereum. Los líderes de Solana calculan árboles de Merkle al crear bloques, pero están estructurados de manera diferente a los de EVM. Desafortunadamente, ahí radica el problema para los rollups basados en SVM.

Solana merkliza transacciones en lo que se llaman entradas, y puede haber múltiples entradas por ranura; por lo tanto, múltiples raíces de transacciones por bloque. Además, Solana calcula una raíz de Merkle del estado de la cuenta solo una vez por época (aproximadamente 2.5 días), y esta raíz no está disponible en el libro mayor.

De hecho, los encabezados de bloque de Solana no contienen ninguna raíz de Merkle en absoluto. En cambio, contienen el anterior y el actual blockhash, que se calcula a través de Solana's Prueba de HistoriaAlgoritmo de prueba de historia (PoH). PoH requiere que los validadores registren continuamente "ticks" mediante el hash recursivo de entradas de bloque, que pueden estar vacías o contener lotes de transacciones. El tick (hash) final del algoritmo PoH es el hash del bloque.

El problema con la Prueba de Historia es que hace que sea muy difícil probar el estado a partir de un bloquehash. Solana está diseñada para transmitir hashes de PoH para mantener su concepto de tiempo transcurrido. La raíz de la transacción solo está disponible para un tick de PoH que contenía una entrada con transacciones, no el bloque en su totalidad, y no hay una raíz de estado almacenada en ninguna entrada.

Sin embargo, existe otro hash que los clientes pueden utilizar: el hash bancario. A veces denominados hash de ranura, los hash bancarios están disponibles en el SlotHashes sysvarcuenta, que puede ser consultada por un cliente. Se crea un hash de banco para cada bloque (ranura) a partir de un puñado de entradas:

  • El hash del banco anterior.
  • El hash delta del estado de la cuenta.
  • El recuento de firmas de transacción en el banco, en bytes.
  • El blockhash de este banco.
  • Solo una vez por época, un hash de todas las cuentas en la base de datos de cuentas.
  • Solo cuando el clúster se haya reiniciado, se generará un hash de cualquier bifurcación dura causada por el reinicio del clúster.

Como se puede ver, el hash del banco está sobrecargado con varios inputs de hash, lo que añade complejidad para los clientes que intentan demostrar información sobre transacciones o estado. Además, solo un hash de banco para un banco que realizó un "hash de cuentas de época" - el hash de todas las cuentas una vez por época - tendrá esa raíz particular incluida en él. Además, la cuenta de sysvar de SlotHashes se trunca a los últimos 512 hashes de banco.

Solución de Merklización de SOON

red SOONEs un SVM L2 en la parte superior de Ethereum. Al integrar la merklización en PRONTO, priorizamos el uso de soluciones probadas y bien establecidas por el bien de la estabilidad, en lugar de reinventar la rueda. Al decidir qué estructura de datos o algoritmos de hash utilizar, consideramos su compatibilidad con los contratos L1 de laPila de Optimismo, su capacidad de integrarse perfectamente en la arquitectura de Solana y si pueden lograr un alto rendimiento bajo el modelo de cuenta de Solana.

Descubrimos que a medida que aumenta el número de cuentas, el modelo de almacenamiento del Trie de Merkle Patricia (MPT) basado en LSM-Treeproduciría más amplificación de lectura/escritura de disco, lo que resultaría en pérdidas de rendimiento. En última instancia, decidimos integrar el Erigon MPTal partir del excelente trabajo de Rust realizado por el rETHequipo y agregando soporte para el modelo de cuenta de Solana.

Arquitectura

Como se mencionó anteriormente, el árbol de estados de SOON es un MPT construido para soportar cuentas de Solana. Como tal, hemos definido un tipo de cuenta compatible con SVM para servir como datos de cada nodo hoja.

struct TrieSolanaAccount {

lamports: u64,

datos: Vec,

ejecutable: bool,

rent_epoch: u64,

propietario: Pubkey,

}

Para habilitar el módulo MPT para suscribirse al último estado de las cuentas SVM en tiempo real, introdujimos un notificador de cuenta. Durante la Etapa Bancaria, el notificador de cuenta informa al módulo MPT de los cambios de estado de la cuenta, y el MPT actualiza incrementalmente estos cambios en la estructura del trie.

Es importante tener en cuenta que el módulo MPT solo actualiza sus subárboles durante cada 50 ranuras y no calcula la raíz del estado al final de cada ranura. Este enfoque se toma por dos razones. Primero, calcular la raíz del estado para cada ranura afectaría significativamente el rendimiento. En segundo lugar, la raíz del estado solo es necesaria cuando el proponente presenta un outputRoota la L1. Por lo tanto, solo necesita ser calculado periódicamente, basado en la frecuencia de presentación del proponente.

outputRoot = keccak256(version, state_root, withdraw_root, l2_block_hash)

El módulo MPT DE SOON mantiene simultáneamente dos tipos de estructuras de trie: el Trie de Estado para el estado global y el Trie de Retiro para transacciones de retiro. El proponente genera periódicamente una raíz de salida por la raíz de estado y la raíz de retiro, y la presenta al L1.

Actualmente, SOON calcula la raíz del estado y la raíz de retiro una vez cada 450 ranuras y la agrega a la cadena de bloques L2. Como resultado, se puede garantizar la consistencia de los datos MPT en otros nodos de la red. Sin embargo, la estructura del bloque Solana no incluye un encabezado de bloque, lo que significa que no hay un lugar para almacenar la raíz del estado. Veamos más de cerca la estructura básica de la cadena de bloques Solana y luego exploremos cómo SOON introduce el UniqueEntry para almacenar la raíz del estado.

La cadena de bloques de Solana está compuesta por ranuras, que son generadas por el módulo PoH. Una ranura contiene múltiples entradas, y cada entrada incluye marcas de tiempo y transacciones. En las capas de red y almacenamiento, una ranura se almacena y transmite utilizando fragmentoscomo la unidad más pequeña. Los fragmentos se pueden convertir en y desde entradas.

[derive(Serialize, Deserialize, Debug, Default, PartialEq, Eq, Clone)]

pub struct Entry {

El número de hashes desde el ID de entrada anterior.

pub num_hashes: u64,

/// El hash SHA-256num_hashesdespués del ID de entrada anterior.

pub hash: Hash,

/// Una lista no ordenada de transacciones que fueron observadas antes de que se registrara el ID de entrada

/// generado. Puede que se hayan observado antes de un ID de entrada anterior pero fueron

/// devuelto a esta lista para garantizar una interpretación determinista del libro mayor.

transacciones pub: Vec,

}

Seguimos la estructura de blockchain generada por PoH y conservamos la estructura de fragmentación, lo que nos permite reutilizar la capa de almacenamiento existente, la capa de red y el marco RPC de Solana. Para almacenar datos adicionales en la cadena de bloques L2, introdujimos el UniqueEntry. Esta característica nos permite personalizar la carga de entrada y definir las reglas de validación independientes para los datos.

pub const UNIQUE_ENTRY_NUM_HASHES_FLAG: u64 = 0x8000_0000_0000_0000;

/// La entrada única es un tipo de entrada especial. Lo cual es útil cuando necesitamos almacenar algunos datos en

/// blockstore pero no quiero verificarlo.

///

/// El diseño de num_hashes es:

/// |...1 bit...|...63 bit...|

/// \ __/

/// \ \

/// bandera campo personalizado

pub trait UniqueEntry: Sized {

fn encode_to_entries(\u0026self) -\u003e Vec\u003cEntry\u003e;

fn decode_from_entries(

entradas: impl IntoIterator<Item = Entry>,

) -> Result;

}

pub fn unique_entry(custom_field: u64, hash: Hash) -> Entry {

Entrada {

   num_hashes: num_hashes(custom_field),   hash,   transactions: vec![],

}

}

pub fn num_hashes(custom_field: u64) -> u64 {

assert!(custom_field < (1 << 63));

UNIQUE_ENTRY_NUM_HASHES_FLAG | campo_personalizado

}

pub fn is_unique(entry: &Entry) -> bool {

entry.num_hashes & UNIQUE_ENTRY_NUM_HASHES_FLAG != 0

}

En un UniqueEntry, num_hashes se utiliza como un diseño de bits, donde el primer indicador de bit se utiliza para distinguir entre una Entry y un Unique Entry, y los siguientes 63 bits se utilizan para definir el tipo de carga útil. El campo de hash sirve como la carga útil, que contiene los datos personalizados requeridos.

Veamos un ejemplo de usar tres entradas únicas para almacenar el hash de la ranura, la raíz del estado y la raíz de retiro.

/// entrada única de raíz MPT.

[derive(Default, Debug, Clone, PartialEq, Eq)]

pub struct MptRoot {

pub slot: Slot,

pub state_root: B256,

pub withdrawal_root: B256,

}

impl UniqueEntry for MptRoot {

fn encode_to_entries(&self) -> Vec {

let slot_entry = unique_entry(0, slot_to_hash(self.slot));   let state_root_entry = unique_entry(1, self.state_root.0.into();   let withdrawal_root_entry = unique_entry(2, self.withdrawal_root.0.into();   vec![slot_entry, state_root_entry, withdrawal_root_entry]

}

fn decode_from_entries(
entradas: impl IntoIterator,
) -> Result {

   let mut entradas = entries.into_iter();   let entrada = entradas.siguiente().ok_or(UniqueEntryError::NoMoreEntries)?;   let ranura = hash_to_slot(entrada.hash);   let entrada = entradas.siguiente().ok_or(UniqueEntryError::NoMoreEntries)?;   let state_root = B256::from(entry.hash.to_bytes());   let entrada = entradas.siguiente().ok_or(UniqueEntryError::NoMoreEntries)?;   let withdrawal_root = B256::from(entry.hash.to_bytes());   Ok(MptRoot { ranura, state_root, withdrawal_root, })

}
}

Dado que UniqueEntry redefine la semántica de num_hashes, no se puede procesar durante la etapa de verificación de PoH. Por lo tanto, al comienzo del proceso de verificación, primero filtramos las entradas únicas y las dirigimos a un flujo de verificación personalizado basado en su tipo de carga útil. Las entradas regulares restantes continúan a través del proceso de verificación original de PoH.

Retiros y Puente Nativo

El puente nativo es una pieza crucial de infraestructura para las soluciones de Capa 2, responsable de la transmisión de mensajes entre la L1 y la L2. Los mensajes de la L1 a la L2 se llaman transacciones de depósito, mientras que los mensajes de L2 a L1 se llaman transacciones de retiro.

Las transacciones de depósito suelen ser gestionadas por el tubería de derivacióny solo requieren que el usuario envíe una sola transacción en el L1. Sin embargo, las transacciones de retiro son más complejas y requieren que el usuario envíe tres transacciones para completar el proceso:

  1. Primero, el usuario debe enviar una transacción de retiro inicial en la L2.
  2. Una vez que se envía el outputRoot que contiene esta transacción de retiro inicial al L1 por el proponente, el usuario debe enviar una prueba de inclusión para la transacción de retiro al L1. Tras verificar con éxito, comienza el período de desafío.
  3. Después de que finalice el período de desafío, el usuario envía una transacción finalizar para completar todo el proceso de retiro.

La prueba de inclusión de una transacción de retiro asegura que el retiro ocurrió en el L2, mejorando significativamente la seguridad del puente canónico.

En la pila OP, el hash de la transacción de retiro del usuario se almacena en la variable de estado correspondiente a la OptimismPortalEl contrato. La interfaz RPC eth_getProof se utiliza luego para proporcionar a los usuarios una prueba de inclusión para una transacción de retiro específica.

A diferencia de EVM, los datos del contrato SVM se almacenan en el campo de datos de las cuentas, y todos los tipos de cuentas existen en el mismo nivel jerárquico.

SOON ha introducido un programa de puente nativo: Bridge1111111111111111111111111111111111111. Cuando un usuario inicia una transacción de retiro, el programa de puente genera un índice globalmente único para cada transacción de retiro y utiliza este índice como una semilla para crear un nuevo Cuenta Derivada del Programa(PDA) para almacenar la transacción de retiro correspondiente.

[derive(Clone, Copy, Debug, PartialEq)]

pub struct WithdrawalTransaction {

/// contador de retiros

pub nonce: U256,

/// usuario que desea retirar

remitente público: Pubkey,

/// dirección de usuario en L1

pub target: Dirección,

/// retirar cantidad en lamports

valor pub: U256,

/// límite de gas en L1

pub gas_limit: U256,

/// datos de retiro en L1

pub data: L1WithdrawalCalldata,

}

Definimos la estructura WithdrawalTransaction para almacenar transacciones de retiro en el campo de datos de la PDA.

Este diseño funciona de manera similar al Stack OP. Una vez que el outputRoot que contiene la transacción de retiro se envía al L1, el usuario puede enviar una prueba de inclusión para la transacción de retiro al L1 para comenzar el período de desafío.

Pruebas de fallos

Después de que el proponente envíe el outputRoot a L1, significa que el estado de L2 se ha resuelto. Si un desafiante detecta que el proponente envió un estado incorrecto, puede iniciar un desafío para proteger los fondos en el puente.

Uno de los aspectos más críticos de la construcción de una prueba de fallas es permitir que la cadena de bloques pase del estado S1 al estado S2 de manera apátrida. Esto garantiza que el contrato de árbitro en L1 pueda reproducir las instrucciones del programa sin estado para realizar el arbitraje.

Sin embargo, al comienzo de este proceso, el retador debe demostrar que todas las entradas iniciales en el estado S1 son válidas. Estas entradas iniciales incluyen los estados de las cuentas participantes (por ejemplo, lamports, datos, propietario, etc.). A diferencia del EVM, el SVM separa naturalmente el estado de la cuenta de la computación. Sin embargo, API SVM de Anzapermite que las cuentas se pasen a través de SVM a través de un rasgo TransactionProcessingCallback, como se muestra a continuación.

pub trait TransactionProcessingCallback {

fn account_matches_owners(&self, account: &Pubkey, owners: &[Pubkey]) -> Option;

fn get_account_shared_data(&self, pubkey: &Pubkey) -> Option;

fn add_builtin_account(&self, _name: &str, _program_id: &Pubkey) {}

}

Por lo tanto, solo necesitamos usar el estado S1 junto con las pruebas de inclusión de las cuentas de entrada para verificar la validez de las entradas del programa de desafío.

Conclusión

SOON está marcando un hito clave para el desarrollo del ecosistema SVM. Al integrar la merklización, SOON aborda la falta de una raíz de estado global de Solana, lo que permite características esenciales como pruebas de inclusión para pruebas de falla, retiros seguros y ejecución sin estado.

Además, el diseño de merklización de SOON dentro de SVM puede habilitar clientes ligeros en cadenas basadas en SVM, aunque la propia Solana no admita actualmente clientes ligeros. Incluso es posible que parte del diseño también ayude a atraer clientes ligeros a la cadena principal.

Utilizando los Árboles de Prueba de Patricia Merkle (MPT) para la gestión del estado, SOON se alinea con la infraestructura de Ethereum, mejorando la interoperabilidad y avanzando en soluciones de capa 2 basadas en SVM. Esta innovación fortalece el ecosistema SVM al mejorar la seguridad, la escalabilidad y la compatibilidad, al tiempo que fomenta el crecimiento de aplicaciones descentralizadas.

Descargo de responsabilidad:

  1. Este artículo es reimpreso de [GateMedio]. Todos los derechos de autor pertenecen al autor original [@0xandrewz y @realbuffalojoe]. Si hay objeciones a esta reimpresión, póngase en contacto con el Gate Learnequipo y lo resolverán rápidamente.
  2. Descargo de responsabilidad: Las opiniones expresadas en este artículo son únicamente las del autor y no constituyen asesoramiento de inversión.
  3. El equipo de Gate Learn tradujo el artículo a otros idiomas. Queda prohibido copiar, distribuir o plagiar los artículos traducidos a menos que se mencione.

Merklización de SVM en SOON

Avanzado1/27/2025, 12:53:10 AM
SOON Network introduce una estructura de árbol de Merkle dentro de la Máquina Virtual Solana (SVM) para abordar el problema de la falta de raíces de estado globales. Esta mejora fortalece las capacidades de SVM en verificación de integridad, pruebas de fraude y operaciones entre capas dentro del sistema de agregación. Al incrustar la raíz del estado directamente en la cadena de bloques SVM, SOON mejora la seguridad y la escalabilidad, proporcionando un soporte más sólido para la agregación SVM.

La Máquina Virtual Solana (SVM) está siendo ampliamente adoptada como la capa de ejecución para varias soluciones de Capa-2 (L2). Sin embargo, una limitación clave en el diseño original de SVM es la oscuridad de su raíz de estado global. Esto plantea desafíos significativos para los sistemas de rollup, donde las raíces de estado global son críticas para garantizar la integridad, permitir pruebas de fraude y respaldar operaciones entre capas.

En un rollup, el proponente envía periódicamente la raíz del estado L2 (raíz de Merkle) a la Capa-1 (L1), estableciendo puntos de control para la cadena L2. Estos puntos de control permiten pruebas de inclusión para cualquier estado de cuenta, lo que permite una ejecución sin estado de un punto de control a otro. Las pruebas de fraude dependen de este mecanismo, ya que los participantes pueden proporcionar pruebas de inclusión para verificar entradas válidas durante disputas. Además, los árboles de Merkle mejoran la seguridad de los puentes canónicos al permitir a los usuarios generar pruebas de inclusión para transacciones de retiro, asegurando interacciones sin confianza entre L2 y L1.

Para abordar estos desafíos, Red SOONintroduce árboles de Merkle en la capa de ejecución de la SVM, lo que permite a los clientes proporcionar pruebas de inclusión. SOON se integra con Prueba-de-Historia utilizando entradas únicas para incrustar raíces de estado directamente en blockchains basados en SVM. Con esta innovación, la pila de SOON puede admitir nuevos rollups basados en SVM con seguridad, escalabilidad y utilidad mejoradas.

El Problema de Merklización de Solana

Solana siempre ha sido diseñada con alta capacidad de procesamiento como objetivo principal, lo que requiere intercambios de diseño deliberados —especialmente durante su desarrollo inicial— para lograr su rendimiento novedoso. Entre estos intercambios, una de las decisiones más impactantes se centró en cómo y cuándo Solana merkleizaría su estado.

Finalmente, esta decisión creó desafíos significativos para los clientes al demostrar el estado global, así como la inclusión de transacciones y la verificación simple de pagos (SPV). La falta de una raíz de estado hash consistentemente representativa del estado SVM merklizado plantea dificultades considerable para proyectos como clientes ligeros y rollups.

Echemos un vistazo a cómo se realiza la merklización en otras cadenas y luego identifiquemos los desafíos presentados por la arquitectura del protocolo de Solana.

Árboles de Merkle en Bitcoin

En Bitcoin, las transacciones se almacenan en un bloque mediante un árbol de Merkle, cuya raíz se almacena en el cabecera del bloque. El protocolo Bitcoin hasheará las entradas y salidas de una transacción (así como algunos otros metadatos) en un ID de transacción(TxID). Para demostrar el estado en Bitcoin, un usuario simplemente puede calcular una prueba de Merkle para verificar el TxID contra la raíz de Merkle del bloque.

Este proceso de verificación también valida el estado, ya que el ID de Tx es único para algún conjunto de entradas y salidas, ambas reflejando cambios en el estado de la dirección. Tenga en cuenta que las transacciones de Bitcoin también pueden contener Scripts de Taproot, que producen salidas de transacción que pueden ser verificadas durante la verificación, a menudo volviendo a ejecutar el script utilizando las entradas de la transacción y los datos de testigos del script, y validando contra sus salidas.

Árboles de Merkle en Ethereum

Similar to Bitcoin, Ethereum stores transactions using a custom data structure (derived from a Merkle tree) called a Trie de Merkle Patricia(MPT). Esta estructura de datos está diseñada para actualizaciones rápidas y optimización en conjuntos de datos grandes. Naturalmente, esto se debe a que Ethereum tiene significativamente más entradas y salidas que gestionar que Bitcoin.

La Máquina Virtual Ethereum(EVM) actúa como una máquina de estado global. El EVM es esencialmente un entorno de computación distribuido gigantesco que admite ejecutable contratos inteligentes, cada uno de los cuales reserva su propio espacio de direcciones en la memoria global. Como resultado, los clientes que desean verificar el estado en Ethereum deben tener en cuenta no solo el resultado de una transacción (logs, código de retorno, etc.) sino también los cambios en el estado global como resultado de la transacción.

Afortunadamente, la EVM hace un uso inteligente de tres estructuras de trie importantes, que almacenan sus raíces en cada encabezado de bloque.

  • trie de estado: Un almacén gigante de pares clave-valor de todos los estados en Ethereum, incluida cada dirección de Ethereum y los datos almacenados dentro de cada cuenta. Para contratos inteligentes, estas cuentas almacenan otro trie llamado una trie de almacenamiento, que es otro mapa clave-valor de todos los datos del contrato inteligente para su espacio de direcciones.
  • Árbol de transacciones: Almacén de valores clave de todas las transacciones en un bloque, donde la clave es la ID de la transacción y el valor es los datos de la transacción.
  • trie de recibos: Trie que contiene los recibos (estado, eventos) de cada transacción en el bloque, hasheados por el índice de cada transacción en el bloque. Cada recibo contiene información sobre la ejecución de la transacción, incluido un hash del estado posterior a la transacción del árbol de estados.

Dada una transacción, un cliente puede demostrar su inclusión en un bloque evaluando la raíz del árbol de transacciones (como Bitcoin), su resultado evaluando el árbol de recibos y los cambios en el estado global evaluando el árbol de estado.

Árboles de Merkle en Solana

Una de las razones por las que Solana tiene una alta capacidad de procesamiento es el hecho de que no tiene la estructura de árbol múltiple que tiene Ethereum. Los líderes de Solana calculan árboles de Merkle al crear bloques, pero están estructurados de manera diferente a los de EVM. Desafortunadamente, ahí radica el problema para los rollups basados en SVM.

Solana merkliza transacciones en lo que se llaman entradas, y puede haber múltiples entradas por ranura; por lo tanto, múltiples raíces de transacciones por bloque. Además, Solana calcula una raíz de Merkle del estado de la cuenta solo una vez por época (aproximadamente 2.5 días), y esta raíz no está disponible en el libro mayor.

De hecho, los encabezados de bloque de Solana no contienen ninguna raíz de Merkle en absoluto. En cambio, contienen el anterior y el actual blockhash, que se calcula a través de Solana's Prueba de HistoriaAlgoritmo de prueba de historia (PoH). PoH requiere que los validadores registren continuamente "ticks" mediante el hash recursivo de entradas de bloque, que pueden estar vacías o contener lotes de transacciones. El tick (hash) final del algoritmo PoH es el hash del bloque.

El problema con la Prueba de Historia es que hace que sea muy difícil probar el estado a partir de un bloquehash. Solana está diseñada para transmitir hashes de PoH para mantener su concepto de tiempo transcurrido. La raíz de la transacción solo está disponible para un tick de PoH que contenía una entrada con transacciones, no el bloque en su totalidad, y no hay una raíz de estado almacenada en ninguna entrada.

Sin embargo, existe otro hash que los clientes pueden utilizar: el hash bancario. A veces denominados hash de ranura, los hash bancarios están disponibles en el SlotHashes sysvarcuenta, que puede ser consultada por un cliente. Se crea un hash de banco para cada bloque (ranura) a partir de un puñado de entradas:

  • El hash del banco anterior.
  • El hash delta del estado de la cuenta.
  • El recuento de firmas de transacción en el banco, en bytes.
  • El blockhash de este banco.
  • Solo una vez por época, un hash de todas las cuentas en la base de datos de cuentas.
  • Solo cuando el clúster se haya reiniciado, se generará un hash de cualquier bifurcación dura causada por el reinicio del clúster.

Como se puede ver, el hash del banco está sobrecargado con varios inputs de hash, lo que añade complejidad para los clientes que intentan demostrar información sobre transacciones o estado. Además, solo un hash de banco para un banco que realizó un "hash de cuentas de época" - el hash de todas las cuentas una vez por época - tendrá esa raíz particular incluida en él. Además, la cuenta de sysvar de SlotHashes se trunca a los últimos 512 hashes de banco.

Solución de Merklización de SOON

red SOONEs un SVM L2 en la parte superior de Ethereum. Al integrar la merklización en PRONTO, priorizamos el uso de soluciones probadas y bien establecidas por el bien de la estabilidad, en lugar de reinventar la rueda. Al decidir qué estructura de datos o algoritmos de hash utilizar, consideramos su compatibilidad con los contratos L1 de laPila de Optimismo, su capacidad de integrarse perfectamente en la arquitectura de Solana y si pueden lograr un alto rendimiento bajo el modelo de cuenta de Solana.

Descubrimos que a medida que aumenta el número de cuentas, el modelo de almacenamiento del Trie de Merkle Patricia (MPT) basado en LSM-Treeproduciría más amplificación de lectura/escritura de disco, lo que resultaría en pérdidas de rendimiento. En última instancia, decidimos integrar el Erigon MPTal partir del excelente trabajo de Rust realizado por el rETHequipo y agregando soporte para el modelo de cuenta de Solana.

Arquitectura

Como se mencionó anteriormente, el árbol de estados de SOON es un MPT construido para soportar cuentas de Solana. Como tal, hemos definido un tipo de cuenta compatible con SVM para servir como datos de cada nodo hoja.

struct TrieSolanaAccount {

lamports: u64,

datos: Vec,

ejecutable: bool,

rent_epoch: u64,

propietario: Pubkey,

}

Para habilitar el módulo MPT para suscribirse al último estado de las cuentas SVM en tiempo real, introdujimos un notificador de cuenta. Durante la Etapa Bancaria, el notificador de cuenta informa al módulo MPT de los cambios de estado de la cuenta, y el MPT actualiza incrementalmente estos cambios en la estructura del trie.

Es importante tener en cuenta que el módulo MPT solo actualiza sus subárboles durante cada 50 ranuras y no calcula la raíz del estado al final de cada ranura. Este enfoque se toma por dos razones. Primero, calcular la raíz del estado para cada ranura afectaría significativamente el rendimiento. En segundo lugar, la raíz del estado solo es necesaria cuando el proponente presenta un outputRoota la L1. Por lo tanto, solo necesita ser calculado periódicamente, basado en la frecuencia de presentación del proponente.

outputRoot = keccak256(version, state_root, withdraw_root, l2_block_hash)

El módulo MPT DE SOON mantiene simultáneamente dos tipos de estructuras de trie: el Trie de Estado para el estado global y el Trie de Retiro para transacciones de retiro. El proponente genera periódicamente una raíz de salida por la raíz de estado y la raíz de retiro, y la presenta al L1.

Actualmente, SOON calcula la raíz del estado y la raíz de retiro una vez cada 450 ranuras y la agrega a la cadena de bloques L2. Como resultado, se puede garantizar la consistencia de los datos MPT en otros nodos de la red. Sin embargo, la estructura del bloque Solana no incluye un encabezado de bloque, lo que significa que no hay un lugar para almacenar la raíz del estado. Veamos más de cerca la estructura básica de la cadena de bloques Solana y luego exploremos cómo SOON introduce el UniqueEntry para almacenar la raíz del estado.

La cadena de bloques de Solana está compuesta por ranuras, que son generadas por el módulo PoH. Una ranura contiene múltiples entradas, y cada entrada incluye marcas de tiempo y transacciones. En las capas de red y almacenamiento, una ranura se almacena y transmite utilizando fragmentoscomo la unidad más pequeña. Los fragmentos se pueden convertir en y desde entradas.

[derive(Serialize, Deserialize, Debug, Default, PartialEq, Eq, Clone)]

pub struct Entry {

El número de hashes desde el ID de entrada anterior.

pub num_hashes: u64,

/// El hash SHA-256num_hashesdespués del ID de entrada anterior.

pub hash: Hash,

/// Una lista no ordenada de transacciones que fueron observadas antes de que se registrara el ID de entrada

/// generado. Puede que se hayan observado antes de un ID de entrada anterior pero fueron

/// devuelto a esta lista para garantizar una interpretación determinista del libro mayor.

transacciones pub: Vec,

}

Seguimos la estructura de blockchain generada por PoH y conservamos la estructura de fragmentación, lo que nos permite reutilizar la capa de almacenamiento existente, la capa de red y el marco RPC de Solana. Para almacenar datos adicionales en la cadena de bloques L2, introdujimos el UniqueEntry. Esta característica nos permite personalizar la carga de entrada y definir las reglas de validación independientes para los datos.

pub const UNIQUE_ENTRY_NUM_HASHES_FLAG: u64 = 0x8000_0000_0000_0000;

/// La entrada única es un tipo de entrada especial. Lo cual es útil cuando necesitamos almacenar algunos datos en

/// blockstore pero no quiero verificarlo.

///

/// El diseño de num_hashes es:

/// |...1 bit...|...63 bit...|

/// \ __/

/// \ \

/// bandera campo personalizado

pub trait UniqueEntry: Sized {

fn encode_to_entries(\u0026self) -\u003e Vec\u003cEntry\u003e;

fn decode_from_entries(

entradas: impl IntoIterator<Item = Entry>,

) -> Result;

}

pub fn unique_entry(custom_field: u64, hash: Hash) -> Entry {

Entrada {

   num_hashes: num_hashes(custom_field),   hash,   transactions: vec![],

}

}

pub fn num_hashes(custom_field: u64) -> u64 {

assert!(custom_field < (1 << 63));

UNIQUE_ENTRY_NUM_HASHES_FLAG | campo_personalizado

}

pub fn is_unique(entry: &Entry) -> bool {

entry.num_hashes & UNIQUE_ENTRY_NUM_HASHES_FLAG != 0

}

En un UniqueEntry, num_hashes se utiliza como un diseño de bits, donde el primer indicador de bit se utiliza para distinguir entre una Entry y un Unique Entry, y los siguientes 63 bits se utilizan para definir el tipo de carga útil. El campo de hash sirve como la carga útil, que contiene los datos personalizados requeridos.

Veamos un ejemplo de usar tres entradas únicas para almacenar el hash de la ranura, la raíz del estado y la raíz de retiro.

/// entrada única de raíz MPT.

[derive(Default, Debug, Clone, PartialEq, Eq)]

pub struct MptRoot {

pub slot: Slot,

pub state_root: B256,

pub withdrawal_root: B256,

}

impl UniqueEntry for MptRoot {

fn encode_to_entries(&self) -> Vec {

let slot_entry = unique_entry(0, slot_to_hash(self.slot));   let state_root_entry = unique_entry(1, self.state_root.0.into();   let withdrawal_root_entry = unique_entry(2, self.withdrawal_root.0.into();   vec![slot_entry, state_root_entry, withdrawal_root_entry]

}

fn decode_from_entries(
entradas: impl IntoIterator,
) -> Result {

   let mut entradas = entries.into_iter();   let entrada = entradas.siguiente().ok_or(UniqueEntryError::NoMoreEntries)?;   let ranura = hash_to_slot(entrada.hash);   let entrada = entradas.siguiente().ok_or(UniqueEntryError::NoMoreEntries)?;   let state_root = B256::from(entry.hash.to_bytes());   let entrada = entradas.siguiente().ok_or(UniqueEntryError::NoMoreEntries)?;   let withdrawal_root = B256::from(entry.hash.to_bytes());   Ok(MptRoot { ranura, state_root, withdrawal_root, })

}
}

Dado que UniqueEntry redefine la semántica de num_hashes, no se puede procesar durante la etapa de verificación de PoH. Por lo tanto, al comienzo del proceso de verificación, primero filtramos las entradas únicas y las dirigimos a un flujo de verificación personalizado basado en su tipo de carga útil. Las entradas regulares restantes continúan a través del proceso de verificación original de PoH.

Retiros y Puente Nativo

El puente nativo es una pieza crucial de infraestructura para las soluciones de Capa 2, responsable de la transmisión de mensajes entre la L1 y la L2. Los mensajes de la L1 a la L2 se llaman transacciones de depósito, mientras que los mensajes de L2 a L1 se llaman transacciones de retiro.

Las transacciones de depósito suelen ser gestionadas por el tubería de derivacióny solo requieren que el usuario envíe una sola transacción en el L1. Sin embargo, las transacciones de retiro son más complejas y requieren que el usuario envíe tres transacciones para completar el proceso:

  1. Primero, el usuario debe enviar una transacción de retiro inicial en la L2.
  2. Una vez que se envía el outputRoot que contiene esta transacción de retiro inicial al L1 por el proponente, el usuario debe enviar una prueba de inclusión para la transacción de retiro al L1. Tras verificar con éxito, comienza el período de desafío.
  3. Después de que finalice el período de desafío, el usuario envía una transacción finalizar para completar todo el proceso de retiro.

La prueba de inclusión de una transacción de retiro asegura que el retiro ocurrió en el L2, mejorando significativamente la seguridad del puente canónico.

En la pila OP, el hash de la transacción de retiro del usuario se almacena en la variable de estado correspondiente a la OptimismPortalEl contrato. La interfaz RPC eth_getProof se utiliza luego para proporcionar a los usuarios una prueba de inclusión para una transacción de retiro específica.

A diferencia de EVM, los datos del contrato SVM se almacenan en el campo de datos de las cuentas, y todos los tipos de cuentas existen en el mismo nivel jerárquico.

SOON ha introducido un programa de puente nativo: Bridge1111111111111111111111111111111111111. Cuando un usuario inicia una transacción de retiro, el programa de puente genera un índice globalmente único para cada transacción de retiro y utiliza este índice como una semilla para crear un nuevo Cuenta Derivada del Programa(PDA) para almacenar la transacción de retiro correspondiente.

[derive(Clone, Copy, Debug, PartialEq)]

pub struct WithdrawalTransaction {

/// contador de retiros

pub nonce: U256,

/// usuario que desea retirar

remitente público: Pubkey,

/// dirección de usuario en L1

pub target: Dirección,

/// retirar cantidad en lamports

valor pub: U256,

/// límite de gas en L1

pub gas_limit: U256,

/// datos de retiro en L1

pub data: L1WithdrawalCalldata,

}

Definimos la estructura WithdrawalTransaction para almacenar transacciones de retiro en el campo de datos de la PDA.

Este diseño funciona de manera similar al Stack OP. Una vez que el outputRoot que contiene la transacción de retiro se envía al L1, el usuario puede enviar una prueba de inclusión para la transacción de retiro al L1 para comenzar el período de desafío.

Pruebas de fallos

Después de que el proponente envíe el outputRoot a L1, significa que el estado de L2 se ha resuelto. Si un desafiante detecta que el proponente envió un estado incorrecto, puede iniciar un desafío para proteger los fondos en el puente.

Uno de los aspectos más críticos de la construcción de una prueba de fallas es permitir que la cadena de bloques pase del estado S1 al estado S2 de manera apátrida. Esto garantiza que el contrato de árbitro en L1 pueda reproducir las instrucciones del programa sin estado para realizar el arbitraje.

Sin embargo, al comienzo de este proceso, el retador debe demostrar que todas las entradas iniciales en el estado S1 son válidas. Estas entradas iniciales incluyen los estados de las cuentas participantes (por ejemplo, lamports, datos, propietario, etc.). A diferencia del EVM, el SVM separa naturalmente el estado de la cuenta de la computación. Sin embargo, API SVM de Anzapermite que las cuentas se pasen a través de SVM a través de un rasgo TransactionProcessingCallback, como se muestra a continuación.

pub trait TransactionProcessingCallback {

fn account_matches_owners(&self, account: &Pubkey, owners: &[Pubkey]) -> Option;

fn get_account_shared_data(&self, pubkey: &Pubkey) -> Option;

fn add_builtin_account(&self, _name: &str, _program_id: &Pubkey) {}

}

Por lo tanto, solo necesitamos usar el estado S1 junto con las pruebas de inclusión de las cuentas de entrada para verificar la validez de las entradas del programa de desafío.

Conclusión

SOON está marcando un hito clave para el desarrollo del ecosistema SVM. Al integrar la merklización, SOON aborda la falta de una raíz de estado global de Solana, lo que permite características esenciales como pruebas de inclusión para pruebas de falla, retiros seguros y ejecución sin estado.

Además, el diseño de merklización de SOON dentro de SVM puede habilitar clientes ligeros en cadenas basadas en SVM, aunque la propia Solana no admita actualmente clientes ligeros. Incluso es posible que parte del diseño también ayude a atraer clientes ligeros a la cadena principal.

Utilizando los Árboles de Prueba de Patricia Merkle (MPT) para la gestión del estado, SOON se alinea con la infraestructura de Ethereum, mejorando la interoperabilidad y avanzando en soluciones de capa 2 basadas en SVM. Esta innovación fortalece el ecosistema SVM al mejorar la seguridad, la escalabilidad y la compatibilidad, al tiempo que fomenta el crecimiento de aplicaciones descentralizadas.

Descargo de responsabilidad:

  1. Este artículo es reimpreso de [GateMedio]. Todos los derechos de autor pertenecen al autor original [@0xandrewz y @realbuffalojoe]. Si hay objeciones a esta reimpresión, póngase en contacto con el Gate Learnequipo y lo resolverán rápidamente.
  2. Descargo de responsabilidad: Las opiniones expresadas en este artículo son únicamente las del autor y no constituyen asesoramiento de inversión.
  3. El equipo de Gate Learn tradujo el artículo a otros idiomas. Queda prohibido copiar, distribuir o plagiar los artículos traducidos a menos que se mencione.
即刻开始交易
注册并交易即可获得
$100
和价值
$5500
理财体验金奖励!