Recientemente comencé a analizar los datos del conjunto UTXO que cada nodo completo almacena en chainstate
una carpeta (una base de LevelDB
datos).
Al observar el código , puede aprender más o menos cómo se formatean las entradas de datos. Sin embargo, para ahorrar tanto espacio como sea posible, algunos de los datos se comprimen y codifican como un archivo varint
.
En el código se puede encontrar y analizar cómo se comprimen los datos . Sin embargo, me cuesta entender cómo se realiza la codificación/descodificación de variantes. De acuerdo con la guía para desarrolladores , la clase responsable de hacerlo es CVarint
, y he podido rastrear el método que creo que lo está haciendo. Sin embargo, dado que no conozco el formato en el que están codificados los datos, no puedo entender lo que está haciendo.
¿Alguien tiene idea de cómo se formatean los datos almacenados?
Aclaración: me refiero al formato CVarint que se usa en UTXO a lo largo de Bitcoin Core, no al formato varint que se usa para codificar información en scripts txs.
El formato CVarInt se implementa en serialize.h
Como el comentario es extenso, lo citaré aquí:
Enteros de longitud variable: los bytes son una codificación MSB base-128 del número. El bit alto en cada byte indica si sigue otro dígito. Para asegurarse de que la codificación sea uno a uno, se resta uno de todos menos el último dígito. Por lo tanto, la secuencia de bytes a[] con longitud len, donde todos excepto el último byte tienen establecido el bit 128, codifica el número:
(a[len-1] y 0x7F) + suma(i=1..len-1, 128^i*((a[len-i-1] y 0x7F)+1))
Propiedades:
- Muy pequeño (0-127: 1 byte, 128-16511: 2 bytes, 16512-2113663: 3 bytes)
- Cada entero tiene exactamente una codificación
- La codificación no depende del tamaño del tipo entero original
- Sin redundancia: cada secuencia de bytes (infinita) corresponde a una lista de enteros codificados.
Ejemplos:
- 0: [0x00]
- 1: [0x01]
- 127: [0x7F]
- 128: [0x80 0x00]
- 255: [0x80 0x7F]
- 256: [0x81 0x00]
- 16383: [0xFE 0x7F]
- 16384: [0xFF 0x00]
- 16511: [0xFF 0x7F]
- 65535: [0x82 0xFE 0x7F]
- 2^32: [0x8E 0xFE 0xFE 0xFF 0x00]
Para almacenar valores de CAmount (enteros que representan números de satoshis), se aplica una transformación de antemano que convierte los números más comunes (múltiplos de potencias de 10) en números más pequeños primero:
MyByteArray& MyByteArray::putVarInt ( const unsigned value )
{
return ( value < 0xFD ) ? putInt8 ( value ) :
( value <= 0xFFFF ) ? putInt8 ( 0xFD ).putInt16 ( value ) :
putInt8 ( 0xFE ).putInt32 ( value );
}
codificación little-endian
MyByteArray& MyByteArray::putPush ( const QByteArray& value )
{
if ( value.size ( ) < OP_PUSHDATA1 )
return putInt8 ( value.size ( ) ).putArray ( value );
if ( value.size ( ) <= 0xFF )
return putInt8 ( OP_PUSHDATA1 ).putInt8 ( value.size ( ) ).putArray ( value );
return putInt8 ( OP_PUSHDATA2 ).putInt16 ( value.size ( ) ).putArray ( value );
}
sr-gi