Documentation Index
Fetch the complete documentation index at: https://mintlify.com/mdiago/VeriFactu/llms.txt
Use this file to discover all available pages before exploring further.
The Blockchain class manages a per-seller (per-NIF) chain of invoice records. Each record’s SHA-256 hash is computed over its data concatenated with the previous record’s hash, creating a tamper-evident audit trail that satisfies the AEAT VERI*FACTU integrity requirements. The chain is persisted as monthly CSV files on disk under the BlockchainPath directory configured in Settings.
Namespace
VeriFactu.Blockchain
Acceso al Blockchain
Blockchain is a singleton keyed by seller NIF (or any agreed identifier). Retrieve or create the chain for a given seller via the static factory method:
var blockchain = Blockchain.Get("B72877814");
If no chain exists yet for that seller, a new one is created and its storage directory is provisioned automatically.
Propiedades públicas estáticas
| Propiedad | Tipo | Descripción |
|---|
Initialized | bool | true once the static constructor has completed loading all seller chains from disk. Set automatically at startup — do not set manually. |
Propiedades públicas de instancia
| Propiedad | Tipo | Descripción |
|---|
SellerID | string | Identificador fiscal del emisor (NIF, VAT Number, DUNS, u otro identificador acordado) al que pertenece esta cadena. |
CurrentID | ulong | Identificador numérico secuencial del último eslabón insertado en la cadena. |
CurrentTimeStamp | DateTime? | Fecha y hora de generación del último eslabón de la cadena. |
Current | Registro | Objeto Registro correspondiente al último eslabón de la cadena. |
PreviousID | ulong | Identificador del penúltimo eslabón (guardado durante la última operación Add). |
PreviousTimeStamp | DateTime? | Fecha y hora del penúltimo eslabón. |
Previous | Registro | Objeto Registro correspondiente al penúltimo eslabón. Disponible inmediatamente después de un Add para permitir rollback. |
BlockchainPath | string | Ruta del directorio donde se almacenan los archivos CSV de esta cadena. Construida como {Settings.Current.BlockchainPath}{SellerID}/. |
BlockchainVarFileName | string | Ruta del archivo CSV de variables en curso (_{SellerID}.csv). Almacena el estado del último eslabón para recargar la cadena desde disco. |
BlockchainDataFileName | string | Ruta del archivo CSV mensual de datos (yyyyMM.csv). Contiene una fila por cada eslabón registrado en el mes en curso. |
BlockchainDataPreviousFileName | string | Ruta del archivo CSV de copia de seguridad del mes en curso (yyyyMM.PREV.csv). Copia del archivo de datos antes del último movimiento; utilizado en operaciones Delete. |
Métodos públicos estáticos
Blockchain.Get(string sellerID)
public static Blockchain Get(string sellerID)
Devuelve la instancia singleton de la cadena de bloques correspondiente al emisor indicado. Si la instancia no existe aún, se crea una nueva y se provisiona su directorio de almacenamiento.
| Parámetro | Tipo | Descripción |
|---|
sellerID | string | NIF, VAT Number, DUNS u otro identificador del emisor de facturas. |
Devuelve: La instancia Blockchain asociada a sellerID.
Blockchain.LoadBlockchainsFromDisk()
public static void LoadBlockchainsFromDisk()
Carga en memoria todas las cadenas de bloques existentes leyendo los subdirectorios y archivos de variables (_*.csv) dentro de Settings.Current.BlockchainPath. Este método es invocado automáticamente por el constructor estático de Blockchain durante la inicialización.
Lanza: InvalidOperationException si Settings.Current.BlockchainPath no está configurado o no corresponde a un directorio válido en disco.
Métodos públicos de instancia
Add(Registro registro)
public void Add(Registro registro)
Añade un único registro a la cadena de bloques. La operación es thread-safe (protegida con lock). Internamente:
- Calcula el
Encadenamiento (referencia al eslabón anterior o marca PrimerRegistro = "S" si es el primero).
- Establece
FechaHoraHusoGenRegistro al momento actual.
- Calcula y asigna
registro.Huella (SHA-256).
- Incrementa
CurrentID y asigna BlockchainLinkID al registro.
- Persiste el estado en disco (
_SellerID.csv y yyyyMM.csv).
| Parámetro | Tipo | Descripción |
|---|
registro | Registro | Instancia de Registro (p. ej. resultado de Invoice.GetRegistroAlta()) a encadenar. |
Lanza: Exception si ocurre cualquier error durante la inserción o escritura en disco.
Add(List<Registro> registros)
public void Add(List<Registro> registros)
Añade una lista de registros en una única operación atómica thread-safe. Todos los eslabones se encadenan secuencialmente y se escriben en el archivo CSV en un solo batch.
| Parámetro | Tipo | Descripción |
|---|
registros | List<Registro> | Lista de registros a encadenar en orden. |
Lanza: Exception si ocurre cualquier error durante la inserción o escritura en disco.
Delete(Registro registro)
public void Delete(Registro registro)
Elimina el último eslabón añadido a la cadena. Se utiliza para rollback cuando un envío a la AEAT falla después de que el registro ya ha sido encadenado. Solo puede eliminarse el último elemento; cualquier intento de eliminar un eslabón anterior lanzará una excepción.
| Parámetro | Tipo | Descripción |
|---|
registro | Registro | El registro a eliminar. Su Huella debe coincidir con la del eslabón Current. |
Lanza:
InvalidOperationException si Settings.Current.DisableBlockchainDelete está a true.
InvalidOperationException si registro.Huella no coincide con Current.Huella (es decir, no es el último eslabón).
Exception si ocurre un error al restaurar el archivo de datos previo en disco.
ToString()
public override string ToString()
Devuelve una representación textual del estado actual de la cadena con el formato:
{SellerID} ({CurrentID}, {NumSerie}, {FechaExpedicion}, {Huella})
Ejemplo: Inicialización manual de la cadena
El siguiente ejemplo, extraído del README oficial, muestra cómo encadenar manualmente dos facturas consecutivas de un mismo emisor:
// Creamos la primera factura
var invoiceFirst = new Invoice("TEST001", new DateTime(2024, 10, 14), "B72877814")
{
InvoiceType = TipoFactura.F1,
SellerName = "WEFINZ GANDIA SL",
BuyerID = "B44531218",
BuyerName = "WEFINZ SOLUTIONS SL",
Text = "PRESTACION SERVICIOS DESARROLLO SOFTWARE",
TaxItems = new List<TaxItem>() {
new TaxItem()
{
TaxScheme = ClaveRegimen.RegimenGeneral,
TaxType = CalificacionOperacion.S1,
TaxRate = 4,
TaxBase = 10,
TaxAmount = 0.4m
},
new TaxItem()
{
TaxScheme = ClaveRegimen.RegimenGeneral,
TaxType = CalificacionOperacion.S1,
TaxRate = 21,
TaxBase = 100,
TaxAmount = 21
}
}
};
// Obtenemos una instancia de la clase RegistroAlta a partir de
// la instancia del objeto de negocio Invoice
var registroFirst = invoiceFirst.GetRegistroAlta();
// Ahora obtenemos el controlador de la cadena de bloques del vendedor
var blockchain = Blockchain.Get(invoiceFirst.SellerID);
// Añadimos el registro de alta
blockchain.Add(registroFirst);
// Creamos la segunda factura y la encadenamos
var invoiceSecond = new Invoice("TEST002", new DateTime(2024, 10, 14), "B72877814")
{
InvoiceType = TipoFactura.F1,
SellerName = "WEFINZ GANDIA SL",
BuyerID = "B44531218",
BuyerName = "WEFINZ SOLUTIONS SL",
Text = "PRESTACION SERVICIOS DESARROLLO SOFTWARE",
TaxItems = new List<TaxItem>() {
new TaxItem()
{
TaxScheme = ClaveRegimen.RegimenGeneral,
TaxType = CalificacionOperacion.S1,
TaxRate = 4,
TaxBase = 10,
TaxAmount = 0.4m
},
new TaxItem()
{
TaxScheme = ClaveRegimen.RegimenGeneral,
TaxType = CalificacionOperacion.S1,
TaxRate = 21,
TaxBase = 100,
TaxAmount = 21
}
}
};
// Obtenemos una instancia de la clase RegistroAlta a partir de
// la instancia del objeto de negocio Invoice
var registroSecond = invoiceSecond.GetRegistroAlta();
// Añadimos el registro de alta
blockchain.Add(registroSecond);
Debug.Print($"La huella de la primera factura es: {registroFirst.GetHashOutput()}");
Debug.Print($"La huella de la segunda factura es: {registroSecond.GetHashOutput()}");
Each call to Add computes the new record’s hash as SHA-256 over the concatenated hash input string that includes the previous record’s Huella, ensuring the chain is tamper-evident.
Almacenamiento en disco
Each seller’s blockchain is persisted under Settings.Current.BlockchainPath/{SellerID}/ using three file types:
| Archivo | Formato | Descripción |
|---|
_{SellerID}.csv | CSV (6 campos) | Estado del último eslabón. Recargado en memoria al arrancar la aplicación. |
yyyyMM.csv | CSV (7 campos) | Registro histórico de todos los eslabones del mes. Un eslabón por línea. |
yyyyMM.PREV.csv | CSV (7 campos) | Copia de seguridad de yyyyMM.csv antes del último movimiento. Usado para restaurar la cadena al eliminar el último eslabón. |
Each row in the monthly data file contains the following semicolon-separated fields:
| Índice | Contenido |
|---|
| 0 | CurrentID — número de eslabón |
| 1 | Marca de tiempo de generación |
| 2 | Huella — hash SHA-256 del registro |
| 3 | Fecha de expedición de la factura |
| 4 | ID del emisor |
| 5 | Número de serie de la factura |
| 6 | Cadena de entrada utilizada para el cálculo del hash (entre corchetes) |
La blockchain se inicializa automáticamente cuando se instancia Settings. No es necesario llamar a Blockchain.Get() manualmente para el uso normal — InvoiceEntry e InvoiceCancellation gestionan el encadenamiento internamente. El acceso directo a Blockchain.Get() es útil principalmente para pruebas, auditorías o integraciones avanzadas.
No modifique manualmente los archivos CSV de la blockchain. La integridad de la cadena depende de que los valores de hash secuenciales se conserven exactamente tal como fueron escritos. Cualquier modificación manual romperá la cadena y hará que los registros afectados no superen la validación de la AEAT.