Formato de serialización CVarint

Recientemente comencé a analizar los datos del conjunto UTXO que cada nodo completo almacena en chainstateuna carpeta (una base de LevelDBdatos).

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.

Respuestas (2)

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:

  • Si la cantidad es 0, salida 0.
  • De lo contrario, divida la cantidad (en unidades base) por la mayor potencia de 10 posible; llamar al exponente e (e es max 9)
  • Si e<9, el último dígito del número resultante no puede ser 0; guárdelo como d, y suéltelo (divida por 10), llamando al resultado n. Luego, salida 1 + 10*(9*n + d - 1) + e
  • Si e==9, solo sabemos que el número resultante no es cero, por lo que genera 1 + 10*(n - 1) + 9
Mi mal, no vi el comentario justo encima del método.
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 );
}
Pero este es el que se usa en las transacciones, por ejemplo, al codificar el número de entradas o salidas. ¿Es lo mismo que se usa cuando se almacena la cantidad de satoshis compactados de un UTXO en LevelDB?
respuesta actualizada con un método para serializar datos en scripts. los satoshis en utxo se atoran como valor de 64 bits de little-endian, no se compactan
Sigue siendo incorrecto. Está preguntando sobre el CVarInt utilizado en UTXO internamente en Bitcoin Core. No los scripts push de longitud variable ni el CCompactLen utilizado en el protocolo P2P.
Exactamente, estoy familiarizado con la codificación varint utilizada en txs serializados pero no con la utilizada en LevelDB.
Ups, lo siento. Mi mala comprensión del inglés. No estoy familiarizado con las fuentes de Bitcoin Core y sus estructuras internas. Por supuesto, mi respuesta fue sobre el protocolo bitcoin en sí.