Siguiente
Continúa con Verificación para recorrer una proof completa desde los datos crudos hasta el commitment on-chain, o con Modos, SLA y finality para saber cómo se programan los batches y se hacen finales.
Esta página especifica, byte a byte, qué compromete CORE-M a la blockchain. Tres implementaciones independientes — el servicio de telemetría, el servicio de verificación, y cualquier verificador externo — deben calcular el hash idéntico a partir de las mismas entradas, por lo que el algoritmo es totalmente determinista y se describe aquí con exactitud. Si tu implementación diverge aunque sea en un solo byte, la verificación fallará por diseño.
Cada punto de telemetría se reduce a un único hash SHA-256 de 32 bytes:
data_hash = SHA256( device_id_utf8 || timestamp_uint64_be || jcs_payload_utf8 )Las tres partes se concatenan en este orden exacto sin separadores y sin prefijos de longitud:
| # | Componente | Codificación | Detalles |
|---|---|---|---|
| 1 | device_id_utf8 | bytes UTF-8 | La cadena device_id, sin terminador nulo. |
| 2 | timestamp_uint64_be | 8 bytes, uint64 big-endian | Época Unix en segundos. Los nanosegundos se truncan. |
| 3 | jcs_payload_utf8 | bytes UTF-8 | El payload serializado con RFC 8785 JCS (más abajo). |
El payload se canonicaliza con el JSON Canonicalization Scheme de RFC 8785 antes de hacerle el hash, de modo que payloads lógicamente idénticos siempre se serialicen a los mismos bytes:
Como las claves se ordenan de forma determinista, el orden en que un dispositivo
emite sus campos no importa: {"temperature":22.5,"humidity":65} y
{"humidity":65,"temperature":22.5} se canonicalizan a exactamente los mismos
bytes y por tanto producen el mismo hash.
Tomemos un punto concreto:
device_id = "D1"timestamp = 1711000000 (segundos Unix){"temperature": 22.5, "humidity": 65}Paso 1 — canonicaliza el payload (JCS). Las claves se ordenan, los espacios en blanco se eliminan:
{"humidity":65,"temperature":22.5}Paso 2 — codifica cada componente.
device_id_utf8 "D1" -> 0x4431timestamp_uint64_be 1711000000 -> 0x0000000065FBC9C0jcs_payload_utf8 {"humidity":65,"temperature":22.5} -> UTF-8 bytes of that stringPaso 3 — concatena en orden y SHA-256.
0x4431|| 0x0000000065FBC9C0|| <UTF-8 of {"humidity":65,"temperature":22.5}>El SHA-256 de esa concatenación es el data_hash de 32 bytes. Este es el valor que
alimenta el Merkle tree y, en última instancia, el commitment on-chain.
Anclar una transacción por punto sería lento y costoso, así que un batch de hashes de puntos se compromete en conjunto mediante un Merkle tree binario SHA-256. Solo el root de 32 bytes va on-chain; cada punto conserva un Merkle path corto que prueba su pertenencia.
Reglas de construcción:
data_hash del batch son las hojas (leaves).parent = SHA256(left || right).flowchart TB R["Root = SHA256(H01 || H23)"] H01["H01 = SHA256(H0 || H1)"] H23["H23 = SHA256(H2 || H3)"] H0["H0 (leaf)"] H1["H1 (leaf)"] H2["H2 (leaf)"] H3["H3 (leaf)"] R --> H01 R --> H23 H01 --> H0 H01 --> H1 H23 --> H2 H23 --> H3
El Merkle path de cada punto es la lista ordenada de hashes hermanos (siblings) necesarios para ascender desde esa hoja hasta el root, cada uno etiquetado con una dirección (si el hermano queda a la izquierda o a la derecha). Un verificador lo reproduce así:
current = data_hashfor each step in merkle_path: if step.is_right: # sibling is on the right current = SHA256(current || step.hash) else: # sibling is on the left current = SHA256(step.hash || current)# current must now equal the Merkle rootPara la hoja H0 en el árbol de arriba, el path es [ {hash: H1, right}, {hash: H23, right} ]: combina con H1 a la derecha para obtener H01, luego con H23 a la
derecha para obtener el root. Recomputar el root a partir de un solo punto — sin
ninguno de los demás puntos del batch — es precisamente lo que hace la proof
portable.
Los paths se calculan en el momento del batch y se almacenan junto a cada hash (y
más tarde en la tabla anchor_proofs de PostgreSQL como JSONB). El recorrido
completo se muestra de principio a fin en
Verificación.
La transacción de anchoring tiene una salida: un OP_FALSE OP_RETURN que lleva un
payload de layout fijo. Tras los opcodes OP_FALSE OP_RETURN, los campos de datos
aparecen en este orden exacto:
| Offset | Campo | Tamaño | Tipo / codificación |
|---|---|---|---|
| 0 | Prefijo de protocolo | 6 bytes | ASCII "CORE-M" |
| 6 | merkle_root | 32 bytes | Merkle root SHA-256 del batch |
| 38 | batch_id | 16 bytes | UUID, binario crudo |
| 54 | timestamp | 8 bytes | uint64 big-endian, segundos Unix |
| 62 | data_point_count | 4 bytes | uint32 big-endian |
El payload total tras los opcodes es de 66 bytes.
OP_FALSE OP_RETURN "CORE-M" 6 bytes, ASCII protocol prefix <merkle_root> 32 bytes, SHA-256 <batch_id> 16 bytes, UUID binary <timestamp> 8 bytes, uint64 big-endian, Unix seconds <data_point_count> 4 bytes, uint32 big-endianEl algoritmo de hashing idéntico se comparte entre tres fronteras — telemetría (calculando hashes para el anchoring), verificación (recomputando a partir de datos crudos), y cualquier verificador externo. Ese determinismo compartido es toda la base de la garantía.
Siguiente
Continúa con Verificación para recorrer una proof completa desde los datos crudos hasta el commitment on-chain, o con Modos, SLA y finality para saber cómo se programan los batches y se hacen finales.