Sharding & Data Availability: Un problema para escalar

Escrito por Pröfesor Utonio, miembro de DeFi LATAM.

En blockchains tradicionales como Bitcoin o Ethereum, el problema de escalar la red a medida que crece su adopción se agrava cada vez más.

En éstas redes, cada nodo es responsable de procesar, validar y guardar todos los datos de la blockchain, por lo que actualmente se ven limitadas a ofrecer entre 10 a 20 transacciones por segundo. Cada bloque que se crea, puede contener un número máximo de transacciones, y cada bloque nuevo es añadido a la red cada cierto tiempo (esto es llamado block time). En Bitcoin, se crea un nuevo bloque cada 10 minutos, y en Ethereum, cada 10-20 segundos.

Éstas limitaciones combinadas con la alta demanda por parte de los usuarios de la red para incluir transacciones en los acortados bloques, llevan a que el costo de transaccionar sea muy elevado.

Sharding - Divide y reinarás

Una de las visiones para hacer escalable una blockchain es mediante el sharding:

La idea detrás de este mecanismo busca partir la blockchain en varias sub-chains llamadas shards, las cuales cuentan con el mismo mecanismo de consenso, la misma arquitectura de construcción de bloques y la misma capacidad. Mediante esta dinámica, en vez de ejecutar una sola blockchain, se ejecutan varios shards en paralelo, conectados entre sí.

Cada shard cuenta con su propio conjunto de validators/miners que crean bloques por separado. Así, no es necesario que absolutamente todos los nodos en la red validen todos los bloques que se crean, sino que cada set de nodos valida las transacciones del shard al que fue asignado. De esta forma, si una blockchain tiene un poder de procesamiento de 10 TPS, al dividir la blockchain en 10 partes iguales logramos escalar hasta 100 TPS.

En lugar de colocar la carga de procesar todas las transacciones en todos los validadores, aplicando el sharding distribuimos la responsabilidad para que se puedan procesar más transacciones sin ralentizar la red.

Los diferentes shards que conforman la blockchain cuentan con una blockchain de ‘coordinación’ en común, la cual no se encarga de validar y procesar bloques, ni cuenta con dapps corriendo sobre ella, sino que más bien actúa como punto de encuentro entre shards. 
Cada cierto tiempo, los diversos shards envían snapshots del estado del shard para que se asienten sobre ésta ‘main-chain’.

Ésta blockchain de coordinación, cuenta con el deber de asignar validadores de forma aleatoria a cada shard y rebalancear el grupo de validadores cada determinado tiempo, procesar el stake que los validadores dejan como depósito en garantía e implementar el mecanismo de slashing de ser necesario.

Aunque cada blockchain cuenta con un sistema diferente, todas implementan una blockchain de conducción: En Ethereum 2.0, esta main-chain es llamada Beacon Chain; en Cosmos, Cosmos Hub; y en Polkadot, Relay Chain.

La función de los nodos en una blockchain

Cada nodo validador en una red es responsable de procesar transacciones correctamente, distribuir las transacciones y bloques válidos hacia los demás nodos que conforman la blockchain y almacenar el historial de la red desde sus inicios de forma local.

En una blockchain, cada bloque que se crea consta de dos partes:

  • Block header: Se puede entender como la información básica del bloque completo pero comprimida para que no ocupe el espacio requerido por el bloque completo. En el block header se incluye el merkle root* para tener certeza que las transacciones incluidas en él son válidas. Si en el block header se incluyesen datos inválidos, el bloque completo sería rechazado, ya que no es posible falsificar los datos que contiene el block body.
  • Block body: contiene todos los datos completos de las transacciones que incluye. Esto requiere almacenar una gran cantidad de datos.

Generalmente, los nodos que corren sobre una blockchain se dividen en dos grupos:

  • Full nodes: descargan cada bloque completo y verifican que cada transacción incluida en los bloques sea válida. Montar un full node requiere un gran espacio de almacenamiento ya que debemos descargar todos los datos históricos de la blockchain.
  • Light nodes: solo descargan el header del bloque que incluye un merkle proof para asegurarse que las transacciones de los bloques que les envían los full nodes sean correctas. Montar un light node es relativamente sencillo, y no requiere mucho almacenamiento, ya que descargamos una versión ‘simplificada’ de la blockchain.
https://arxiv.org/pdf/1809.09044.pdf
https://arxiv.org/pdf/1809.09044.pdf

Vulnerabilidades del mecanismo

En una blockchain tradicional (Bitcoin o Ethereum) cada nodo debe verificar que cada transacción que se genera es correcta. Por lo que, si un atacante quiere incluir una transacción incorrecta en un bloque, deberá controlar el 51% de los nodos en la red para incluirla exitosamente. Poniendo como ejemplo una red que contiene 100 validadores, para atacar la blockchain creando transacciones inválidas o censurando algunas otras, es necesario dominar 51 validadores para hacerlo de forma exitosa.

Sin embargo, en una blockchain que implementa el sharding, corromper la red se torna un poco más fácil de lograr: ya que efectivamente la blockchain se divide en varias partes y se asigna un grupo de nodos a cada shard específico, la seguridad de la red no es la misma que si todos los nodos que conforman la blockchain estuviesen verificando cada bloque que se produce en toda la red.

Siguiendo el ejemplo anterior, supongamos que la blockchain ahora se parte en 10 shards. En este escenario, para corromper un shard no es necesario estar en control del 51% del hashrate total, sino más bien del 5.1% (51%/10) del total de nodos en la red. Con esta visión, es mucho más probable un ataque del 51% en un solo shard que un ataque del 51% en toda la red. Siguiendo esta línea, podemos agregar más y más shards y el número necesario para corromperla seguirá disminuyendo. Por lo tanto, la seguridad del protocolo disminuye cada vez más a medida que se agregan shards a la red, haciendo que cada shard sea más fácil de atacar individualmente.

Randomness

Por suerte, existen soluciones que apuntan contra esta debilidad del mecanismo: Si los validadores/mineros no pueden elegir a qué shard aplican su hash power/stake, se vuelve poco probable que una entidad en control del 5.1% de los nodos sea asignada a un mismo shard, volviéndola corruptible. Para asegurar la integridad y seguridad de cada shard, es indispensable contar con un sistema de elección aleatoria de los validadores que lo conforman y así reducir las probabilidades de comprometer el sistema.

En Ethereum 2.0, esta elección aleatoria se genera a través del mecanismo llamado VDF (Función de Retardo Verificable), de la cual la blockchain ‘de conducción’ (Beacon Chain) es la encargada de asignar validadores a cada shard.

Beacon Chain avanza por epochs, los cuales contienen 32 slots (cada slot es una oportunidad para incluir un nuevo bloque). En Beacon Chain, los validadores se dividen por comités, los cuales deben contener por lo menos, 128 validadores en cada comité. Los validadores, agrupados por comités, son asignados a cada shard al inicio de cada epoch.

Las funciones de retraso verificables están diseñadas para retrasar la revelación sobre a qué comités fueron designados los validadores hasta el punto en que los actores maliciosos no puedan usar la información para beneficiarse, reduciendo la probabilidad de corromper un mismo shard.

https://vitalik.ca/general/2020/04/07/sharding.html
https://vitalik.ca/general/2020/04/07/sharding.html

Otra forma de generar aleatoriedad en blockchain es a través del sistema VRF (Función de aleatoriedad verificable). Chainlink y Algorand son algunos ejemplos que lo implementan.

Data Availability aplicado en sharding

El mecanismo de sharding implica que los nodos que conforman la red no deben necesariamente validar absolutamente todos los bloques de todos los shards. Como tal, cada vez que un usuario necesita interactuar con un shard diferente al cual se encuentra, no puede descargar y validar el historial completo del shard al cual apunta.
Este problema desencadena una pregunta fundamental: sin descargar y validar el historial completo de un shard en particular, ¿Cómo puede un usuario estar seguro que el historial del shard con el que interactúa es correcto y no se omitió ningún dato?

¿Cómo pueden los nodos de una red verificar que todos los datos de un nuevo bloque están realmente disponibles y que ningún dato ha sido ocultado o censurado? Si los datos no están completamente disponibles para ser verificados, el bloque puede contener transacciones maliciosas que el block producer ha ocultado.

Tomando la definición de full node y light node explicada más arriba, podemos aplicar el mismo criterio en sharding: los validadores asignados en cada shard verifican todas las transacciones de cada bloque en ese shard específico, pero no lo hacen de forma completa en los demás shards que conforman la red, solo verifican el block header de los demás shards. Sabiendo esto, podemos inferir que el grupo de validadores aplicado a cada shard opera como un full node chequeando el estado completo del shard, mientras que en los demás shards, actúan como light node.
Por lo general, un nodo en una blockchain con sharding ejecutará un full node solo para uno o unos pocos shards, y correrá un light node para los distintos shards que componen la red.**
**

¿Cuál es el problema? Ya que los validadores en un shard no pueden estar 100% seguros que los bloques creados en otro shard del que no forman parte como full nodes (más bien, operando como light nodes) contienen los datos completos en el bloque, no tienen la certeza que nada está siendo ocultado/censurado por los demás en otros shards. Por lo tanto, es indispensable contar con alguna manera de confirmar que en ningún momento de la historia en ningún shard se incluyó algún bloque inválido.

Para detectar si algún shard incluyó en un bloque una transacción inválida, es necesario poder garantizar que todos los datos en ese shard se publicaron y estuvieron disponibles para todos en la red.

La solución simplificada indicaría que todos los nodos en todos los shards deberían actuar como full nodes de cada shard en la red, pero esto rompería el propósito del sharding al ser necesario que todos los nodos almacenen una cantidad muy grande de datos. Esto sería posible en una blockchain diseñada con pocos shards, pero para escalar hasta decenas y decenas de shards, requiere una enorme cantidad de almacenamiento. No es razonable esperar que los nodos cuenten con el historial completo de todos los shards, por lo tanto no es una solución factible.

Algunas soluciones para este problema que no discutiremos en profundidad en este blog se tratan de Notaries, Erasure code, Validium y Volition.

  • Notaries: éstos van rotando entre shards de forma más frecuente que los validadores para verificar el estado completo de los bloques y publican una prueba de su accionar.
  • Erasure code: mediante este mecanismo, es posible recuperar el estado del bloque completo, incluso si solo una parte del bloque fue publicado.
  • Validium: a diferencia de los zk-rollups, Validium guarda los datos off-chain.
  • Volition: permite a los usuarios cambiar entre un zk-rollup y Validium y admite el almacenamiento off-chain u on-chain de los datos.

Transacciones cross-shard:

Ejemplo 1) Supongamos el siguiente escenario: una blockchain se ha partido en 2 shards, pero el shard 1 contiene un conjunto de validadores coludiendo para incluir transacciones inválidas. Éstos, comienzan a producir un bloque malicioso B y logran insertarlo en el shard 1.

Una vez incluido el bloque B en el shard #1, los validadores intentan hacer una transacción cross-shard (entre shards) hacia el shard #2 para que así, su bloque sea incluido en un shard válido, y las transacciones incluidas en el bloque B tengan más chances de pasar desapercibidas. Pero este accionar no sería posible, ya que si bien los light nodes del shard #2 no verifican los datos completos del shard #1, sí reciben el header del bloque B (el cual no se puede adulterar) del shard #1. Por lo tanto, los nodos del shard #2 rechazarían el bloque inválido del shard #1.

Cross-shard tx inválida
Cross-shard tx inválida

Ejemplo 2) Siguiendo la línea del ejemplo 1: Ahora bien, ¿Qué pasaría si los nodos maliciosos incluyesen un bloque C válido por encima del bloque B inválido que han creado anteriormente?

De ser así, una transacción cross-shard desde el shard 1 hacia el shard 2 podría ser incluída, ya que aparentemente para los nodos del shard 2, el bloque C (que incluye el bloque B) es válido.

Teniendo esto en cuenta, que los nodos del shard de destino verifiquen el estado completo del bloque del shard desde el cual se inicia la transacción, no es una solución viable.

Cross-shard tx válida
Cross-shard tx válida

La visión de Kadena para lidiar con este problema: Chainweb

El sistema que utilizan para escalar de forma horizontal es sencillo pero eficaz: unir shards entre sí de manera que cada shard se conecte con sus shards vecinos y otros shards más alejados, y solo permitir transacciones cross-shard entre los shards vecinos conectados.

El protocolo requiere que cada bloque en cada shard incluya el blockhash del bloque anterior del mismo shard, pero además exige que se incluya el blockhash del bloque anterior de todos los shards con los cuales se conecta directamente.

Cada nodo en los diversos shards, cuentan con el deber de validar tanto los bloques en el shard en el que se encuentra como todos los bloques de todos los shards vecinos.

https://medium.com/kadena-io/how-to-scale-a-proof-of-work-blockchain-9233e5b4b62
https://medium.com/kadena-io/how-to-scale-a-proof-of-work-blockchain-9233e5b4b62

Para efectuar una transacción cross-shard entre shards que no son vecinos, dicha transacción se enruta a través de varios shards en la red. Cada shard se conecta con los 3 shards vecinos de forma multidireccional. Por lo tanto, para saltar desde cualquier shard hacia cualquier otro shard, solamente es requisito pasar por no más de 2 o 3 shards hasta llegar al shard deseado.

Para asegurarse que todo el hashpower/stake de la red esté protegiendo el bloque en el cual se incluye nuestra transacción, debemos esperar la inclusión de por lo menos 3 bloques luego del nuestro. Por lo que, como sucede en una blockchain tradicional, para atacarla/censurarla se debe estar en posesión del 51% del hashpower/stake.

Volviendo al ejemplo 2: si los nodos en el shard 1 comienzan a coludir para incluir transacciones inválidas en el bloque B, luego crean un bloque C válido e intentan iniciar una transacción cross-chain hacia el shard 2, ésta no se efectuará debido a que los nodos en el shard 2 han validado el estado completo del shard 1, rechazando el bloque malicioso.

Llegaste hasta aca? Gracias por leer: sharding, ya sabes que hacer… link

Cross-shard tx en Chainweb
Cross-shard tx en Chainweb

Fuentes y links de utilidad:

Subscribe to SEED Latam
Receive the latest updates directly to your inbox.
Verification
This entry has been permanently stored onchain and signed by its creator.