Schema de atestación
Toda atestación de energía sigue un único schema registrado en el SchemaRegistry de EAS. El schema define la estructura de datos a la que deben ajustarse todas las atestaciones.
Definición del schema
uint64 projectId, uint32 readingCount, uint32 readingIntervalMinutes, uint256[] readings, uint64 fromTimestamp, string method, string metadataURIReferencia de campos
| Campo | Tipo | Descripción |
|---|---|---|
| projectId | uint64 | ID del proyecto de energía registrado |
| readingCount | uint32 | Número de lecturas por intervalo (debe ser igual a readings.length) |
| readingIntervalMinutes | uint32 | Duración de cada intervalo en minutos |
| readings | uint256[] | Energía por intervalo en Wh (la longitud del arreglo debe coincidir con readingCount) |
| fromTimestamp | uint64 | Inicio del período reportado (segundos Unix) |
| method | string | Método de recolección: "manual", "iot", "estimated", etc. |
| metadataURI | string | URI opcional a la evidencia de respaldo (IPFS o HTTPS) |
El tipo de energía se almacena por proyecto en el registro (se define al registrarlo), no por atestación. El resolver lee el tipo de energía del proyecto desde el registro durante la validación.
Valores derivados
El resolver calcula dos valores a partir de los datos de la atestación. No se almacenan en la atestación, pero el registro los utiliza:
toTimestamp = fromTimestamp + readingCount * readingIntervalMinutes * 60
energyWh = sum(readings)Codificación ABI
Los datos de la atestación se codifican en ABI antes de enviarse a EAS:
import { AbiCoder } from 'ethers';
const data = AbiCoder.defaultAbiCoder().encode(
[
"uint64", // projectId
"uint32", // readingCount
"uint32", // readingIntervalMinutes
"uint256[]", // readings
"uint64", // fromTimestamp
"string", // method
"string" // metadataURI
],
[
1, // projectId
3, // 3 readings
60, // 60-minute intervals
[100000, 150000, 200000], // readings in Wh
1700000000, // fromTimestamp
"iot", // method
"" // metadataURI (optional)
]
);Unidades de energía
Todos los valores de energía están en vatios-hora (Wh), no en kWh ni MWh. Esto evita problemas de precisión de punto flotante on-chain.
| Unidad | Valor en Wh | Ejemplo |
|---|---|---|
| 1 Wh | 1 | Una bombilla LED durante una hora |
| 1 kWh | 1,000 | Consumo horario típico de un hogar |
| 1 MWh | 1,000,000 | Consumo diario de una instalación comercial pequeña |
| 1 GWh | 1,000,000,000 | Producción mensual de una planta solar a escala de red |
Bloqueo de períodos
El registro aplica dos restricciones de unicidad por proyecto:
| Restricción | Clave | Propósito |
|---|---|---|
| Unicidad de período | (projectId, fromTimestamp, toTimestamp) | Evita que la misma ventana de tiempo se atestigüe dos veces |
| Unicidad de inicio | (projectId, fromTimestamp) | Evita dos atestaciones con el mismo inicio, incluso con duraciones distintas |
Cadena lineal obligatoria
Las atestaciones de un proyecto deben formar una cadena lineal: el fromTimestamp de cada nueva atestación debe ser igual al toTimestamp de la atestación anterior. Esto evita huecos o solapamientos en la línea de tiempo de reportes.
Si un proyecto no genera ni consume energía durante un período, igualmente debe enviarse una atestación de energía cero (con todas las lecturas en 0) para mantener la cadena. Omitir un período rompería la secuencia lineal y bloquearía todas las atestaciones futuras de ese proyecto.
Si los datos de energía de un período no están disponibles temporalmente (por ejemplo, una falla del medidor o una exportación de datos retrasada), envía una atestación de energía cero como marcador para que la cadena siga avanzando. Cuando los datos reales estén disponibles, reemplaza la atestación en cero con las lecturas correctas mediante el mecanismo de reemplazo.
Correcciones mediante reemplazo
Para corregir una atestación, envía una atestación de reemplazo estableciendo el campo refUID con el UID de la atestación original. El resolver valida que el reemplazo cubra el mismo proyecto y período; luego el registro anota el reemplazo y ajusta los acumuladores de energía según la diferencia. Las revocaciones directas están bloqueadas.