Garantizar la integridad de los datos con hash criptográfico y la cadena de bloques Ethereum

En los flujos de trabajo científicos, los equipos a menudo necesitan acceso a un conjunto de datos compartido que permanece perfectamente sincronizado y no se puede modificar, por ejemplo, en entornos distribuidos de aprendizaje automático donde varios equipos dependen exactamente del mismo conjunto de funciones.

En este artículo, explicaré un método simple y gratuito para cifrar criptográficamente un conjunto de datos de cualquier tamaño y almacenar su hash de manera inmutable en la cadena de bloques Ethereum, creando un registro permanente y verificable de la integridad del conjunto de datos.

Este método también se puede extender simplemente a pesos de modelos, transformaciones específicas que deben aplicarse de manera consistente, código fuente u otros datos que deben ser inmutables y verificables.

🤔Por qué es importante la integridad

Si está al menos algo familiarizado con la ciencia de datos como práctica, ya será consciente de la importancia de la integridad de los datos. Incluso pequeños cambios o errores en los datos de entrada pueden colapsar un proyecto.

Los modelos modernos de aprendizaje automático son extremadamente sensibles a sus datos de entrenamiento. La falta de pasos de normalización, un archivo CSV modificado, filas mezcladas, funciones dañadas o discrepancias entre los conjuntos de datos de entrenamiento y validación pueden producir resultados dramáticamente diferentes.

Las fallas de integridad son difíciles de detectar y, a menudo, descarrilan.

Es posible que los modelos todavía parezcan funcionar normalmente o entrenarse, pero las métricas pueden degradarse lentamente, aumentar la deriva o los experimentos volverse imposibles de reproducir. La integridad es doblemente importante cuando el equipo está distribuido, posiblemente en diferentes organizaciones, y necesita trabajar en diferentes versiones del mismo problema.

🔐Uso de un hash criptográfico como “fuente de verdad”

Un hash criptográfico nos brinda un mecanismo simple y muy útil para verificar la integridad de los datos.

Una breve introducción a los hashes criptográficos

Una función hash toma cualquier cantidad de datos de entrada (bytes) y produce de manera determinista una salida de longitud fija conocida como hash o resumen. Los hashes criptográficos son fundamentales en la informática, como probablemente ya sepa.

La clave es el determinismo:

Mismos datos en → mismo hash out

Incluso un solo byte modificado en los datos de entrada produce un hash completamente diferente.

Debido a esta propiedad, los hashes actúan como huellas digitales únicas para los datos y son extremadamente útiles para verificar la integridad. Hay muchos tipos de funciones hash y algunas son más útiles para esta tarea, como describiré.

¿Cómo se aplica esto a los conjuntos de datos?

Debido al determinismo de la función hash, una vez aplicada a un conjunto de datos, podemos probar de forma rápida y confiable si el conjunto de datos es idéntico a lo que esperamos.

Esto es excepcionalmente valioso con grandes conjuntos de datos que utilizan varios equipos y empresas y pasan de una versión a la siguiente. El equipo 1 del Grupo de Investigación Alpha crea las funciones 1-10, el Equipo 2 del Grupo de Investigación Zeta crea las funciones 10-100, el Sistema X consume la versión Y, etc.

Ya no necesitamos cuestionar los datos, simplemente calculamos la función hash sobre el conjunto de datos y la comparamos con el hash calculado en un punto de referencia. Si coincide, está bien. Si no, algo ha cambiado.

El hashing es extremadamente eficiente. La ejecución de una función hash en un conjunto de datos de 10 MB o 10 TB nos proporciona rápidamente una cadena pequeña de tamaño fijo que se puede compartir, almacenar o publicar.

🧐 ¿Por qué utilizar Ethereum como tienda inmutable?

Esta es la parte realmente útil de este artículo.

Ethereum, nuevamente, como ya sabes, es una cadena de bloques. Esto nos da:

Inmutabilidad: una transacción nunca se puede cambiar Disponibilidad distribuida: siempre accesible sin una autoridad central Permanente: una vez escrita, es accesible permanentemente

Pero, ¿Ethereum es para transacciones? ¿No necesitamos escribir un contrato inteligente complicado para este propósito especializado?

De hecho podrías. Pero no es necesario.

Lo inteligente es utilizar este campo de datos de entrada de uso poco común en una transacción de Ethereum, a veces denominado “datos de llamada”.

Pero, ¿las transacciones de Ethereum cuestan dinero real (gasolina, tarifas, etc.)?

También es cierto. En Ethereum, se le cobra “gas” por cada byte de los datos de entrada. En la red principal, con un precio de $2000 por ETH, esto podría costarnos entre $0,04 y $0,10 por hash. Esto no incluye el gas necesario para que un validador de bloque incluya una transferencia real, que puede ser considerable dependiendo de la carga actual de la red.

Hagamos esto más inteligente. 🦊

Al descargar todo a la “testnet”, que comúnmente tiene cada cadena de bloques, podemos hacer que esto sea completamente gratuito.

Sepolia (la red de prueba de ETH) rara vez se utiliza a menos que sea un desarrollador de contratos inteligentes. Sepolia ETH es gratuito y está disponible públicamente en faucets.

Esto significa que podemos crear una cantidad infinita de transacciones, en la red de prueba de acceso público (llamada Sepolia para Ethereum), ¡de forma gratuita!

Siempre que nuestros datos de entrada tengan un tamaño razonable, Sepolia proporciona una manera de utilizar blockchain para un almacenamiento infinito de datos, con prácticamente las mismas propiedades que la red principal*

* Las cadenas de bloques de Sepolia no son permanentes, pero en su mayoría son confiables durante varios años. Si necesita permanencia absoluta, deberá pagarla mediante la red principal.

Recuerde, no almacenamos los datos reales en la cadena. Sólo la huella digital.

⚙️El proceso

Primero, necesitamos una forma de crear transacciones de manera confiable en Ethereum.

Aunque parezca complejo, en realidad es sumamente sencillo. No necesitamos ningún software adicional ni tecnología de billetera. Una billetera no es más que una clave, combinada con un secreto que se utiliza para firmarla.

Para crear una transacción de Ethereum, creamos un objeto Python con las claves y el formato requeridos, lo codificamos con nuestra clave y lo transmitimos a la red. Luego, un validador recoge nuestra transacción del “mempool” y la incluye en un bloque.

Siempre que incluyamos todos los campos obligatorios y se verifique, ahora será una parte permanente de la cadena de bloques en aproximadamente 12 segundos.

Paso 1: crea la clave y el secreto con web3.py con unas pocas líneas de código

from eth_account import Cuenta cuenta = Cuenta.create() print(“Dirección:”, cuenta.dirección) print(“Clave privada:”, cuenta.key.hex())

Paso 2: obtenga algo de ETH en Sepolia. Ingrese su dirección aquí y espere 12 segundos. ¡Gracias Google!

Paso 3: hash del conjunto de datos

Como mencioné, hay algunos hashes que son mejores para este proceso. Podríamos usar un hash SHA256, pero Blake2b en realidad es mejor en términos de rendimiento. Realmente, cualquier función hash funcionará.

Utilice esta función para codificar los datos. importar hashlib desde pathlib importar ruta def hash_dataset(dataset, algoritmo=”blake2b”, chunk_size=1024 * 1024): h = hashlib.new(algorithm) def update(obj): if isinstance(obj, (str, Path)) y Path(obj).exists(): con open(obj, “rb”) como f: mientras que chunk := f.read(chunk_size): h.update(chunk) elif isinstance(obj, bytes): h.update(obj) elif isinstance(obj, str): h.update(obj.encode(“utf-8”)) elif isinstance(obj, dict): for k in sorted(obj.keys()): update(k) update(obj[k]) elif isinstance(obj, (list, tuple)): para el elemento en obj: actualizar(item) elif isinstance(obj, set): intente: para el elemento en sorted(obj): actualizar(item) excepto TypeError: para el elemento en sorted(obj, key=str): actualizar(item) elif hasattr(obj, “__iter__”): para el elemento en obj: actualizar(item) else: h.update(repr(obj).encode(“utf-8”)) update(dataset) return h.hexdigest() digest = hash_dataset(“hugedataset.parquet”, algoritmo=”blake2b”)

Paso 4: Escribe, firma y publica una transacción con el hash de nuestro conjunto de datos.

Usando la biblioteca web3.py, podemos estructurar nuestra transacción como un dictado de Python y luego publicarla en la red.

Necesitamos un proveedor para transmitir nuestra transacción (no tenemos un nodo). Aquí usamos Infura, pero hay otros, como Alchemy.

Solo tenga en cuenta que agregamos un bit cero “0x” al hash calculado en nuestro conjunto de datos. Necesitamos eliminarlo cuando validemos nuestro hash.

desde web3 importar Web3 w3 = Web3(Web3.HTTPProvider(“https://sepolia.infura.io/v3/YOUR_KEY”)) dataset_hash = “0x” + resumen cuenta = w3.eth.account.from_key(“YOUR_PRIVATE_KEY”) tx = { “to”: cuenta.dirección, # autoenvío (no se requiere contrato) “valor”: 0, # sin transferencia ETH “gas”: 50_000, “maxFeePerGas”: w3.to_wei(“20”, “gwei”), “maxPriorityFeePerGas”: w3.to_wei(“2”, “gwei”), “nonce”: w3.eth.get_transaction_count(cuenta.dirección), “chainId”: 11155111, # Sepolia testnet “data”: dataset_hash }

Fírmalo y envíalo. Aquí, esperamos hasta que finalice la transacción.

signed_tx = account.sign_transaction(tx) tx_hash = w3.eth.send_raw_transaction(signed_tx.rawTransaction) print(“Broadcast tx hash:”, tx_hash.hex()) # Esperar a que se extraiga / se incluya en un bloque tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash) print(“Transacción extraída en el bloque:”, tx_recibo[“blockNumber”]) print(“Estado:”, tx_recibo[“status”])

Asegúrese de conservar la identificación de la transacción.

Paso 5: cree un registro de metadatos para almacenar junto con nuestro conjunto de datos

Aquí, creamos una pieza simple de metadatos, que se puede almacenar en una base de datos (DynamoDB, MongoDB) o directamente junto con nuestro objeto de datos (S3, Google Cloud Storage).

Los metadatos podrían verse así:

{ “dataset_id”: “feature_set_v42”, “dataset_uri”: “s3://ml-bucket/features/v42.parquet”, “dataset_hash”: “0x9f3c…ab21”, “tx_hash”: “0x7c1a…e91d”, “timestamp_unix”: 1730000000, “hash_algorithm”: “blake2b”, “creator”: “0xabc123…”, “notes”: “funciones normalizadas” }

Paso 6: Siempre que lea el conjunto de datos, valide que el hash coincida con el hash original almacenado junto con nuestros datos.

El paso final del proceso combina tres acciones:

Obtenga la transacción de Ethereum. Extraiga el hash del conjunto de datos de calldata. Compárelo con un hash recalculado localmente desde web3.[“input”]
onchain_hash = raw_input.hex () si hasattr (raw_input, ‘hex’) else str (raw_input). {onchain_hash}”) print(“Verificación de integridad PASADA. El conjunto de datos coincide con el registro de blockchain.”) return True

¡Eso es todo!

Una nota importante: esto no impide que nadie reescriba nuestro objeto de metadatos. Sin embargo, hay muchas formas de evitar la modificación interna de una pequeña porción de metadatos, como bases de datos de auditoría o S3 Object Lock.

Concluyendo

En última instancia, utilizar un hash criptográfico para verificar la integridad del conjunto de datos es un enfoque ligero para un problema grave.

Algunas extensiones naturales de esto incluyen el uso de este método para verificar los pesos del modelo, o incluso el hash de piezas de código fuente para garantizar que el preprocesamiento sea relevante.

Ya sea que esté colaborando entre equipos distribuidos de código abierto, realizando investigaciones reproducibles o simplemente creando una pista de auditoría para el cumplimiento, blockchain es un notario amable e imparcial para sus datos. No es necesario confiar en la infraestructura; sólo necesitas confiar en las matemáticas.