state trie

Ethereum: explorando base de datos leveldb

Después de todo una blockchain guarda los datos y los bloques en archivos en el ordenador donde se ejecuta el nodo. En el caso del cliente de Ethereum Geth, escrito en el lenguaje go, se usa una base de datos leveldb (de Google, y también en otras blockchain).

Aunque los clientes de blockchain ofrecen APIs para acceder a los datos almacenados (consultas de bloques, balances, transacciones, etc), es bueno entender cómo se guarda todo esto en la base de datos leveldb.

Partiremos de un caso de una blockchain privada con el cliente geth, y de las herramientas en go, tomando como base los anteriores posts Ethereum Blockchain privada y Ethereum y LevelDB: Leer, Modificar y Compilar Geth.

Lanzaremos la blockchain privada con dos nodos validadores (protocolo clique), uno de ellos tendrá fondos en el bloque génesis, y efectuaremos una transacción a otra cuenta. Con go volcaremos toda la base de datos (parando la blockchain previamente) y veremos los pares key,value que se almacenan y cómo interpretarlos

Entorno windows portable

Podemos crear un entorno portable en windows (sin instalar nada), y necesitamos lo siguiente (descomprimiéndo cada archivo descargado):

  • Go: versión zip https://go.dev/dl/ versión go.x.x.xwindows-amd64.zip
  • Mingw (compilador C) tiene que ser la versión MSVCRT no la UCRT, pues da error. Los binarios están en https://www.mingw-w64.org/downloads/, por ejemplo el de winlibs funciona https://winlibs.com/  (versión 12.2.0 MSVCRT)
  • Git, aunque no es estrictamente necesario, pues se usa para descargar el código fuente de Geth https://git-scm.com/download/win, también proporciona una shell bash que funciona muy bien en windows y permite usar comandos de linux muy útiles (grep, diff, etc)

Para que todo funcione correctamente, hay que definir unas variables de entorno para agregar al PATH de windows, que podemos hacer con el siguiente archivo bat, en donde se ejecuta la shell bash de git (hay que cambiar el directorio de trabajo con la variable de entorno %HOME%):

set gccPATH=%~dp0mingw_winlibs\bin
set goPATH=%~dp0go\bin
set gitPATH=%~dp0git\bin
set path=%PATH%;%gccPATH%;%goPATH%;%gitPATH%
set HOME=%~dp0
start git\git-bash.exe --cd-to-home

Con lo anterior ya podemos compilar geth desde su código fuente y ejecutar scripts en go, tal como describía en los posts anteriores. (Nota: los paquetes go los pone en el directorio %goPATH%)

Script en go para volcar toda la base de datos LevelDB

El siguiente script (presente en la web del paquete leveldb para go https://github.com/syndtr/goleveldb) vuelca todos los valores de la base de datos leveldb. La ruta (path) a la blockchain de ethereum está en la carpeta donde hemos indicado que se almacene.

package main

import (
	"fmt"
	"github.com/syndtr/goleveldb/leveldb"
)


func main() {
	// Connection to leveldb
	db, _ := leveldb.OpenFile("./eth_network/geth/chaindata", nil)


	iter := db.NewIterator(nil, nil)
	for iter.Next() {
		// Remember that the contents of the returned slice should not be modified, and
		// only valid until the next call to Next.
		key := iter.Key()
		value := iter.Value()
		fmt.Printf("Key: %x \nValue: %x\n\n\n", key, value)

	}
	iter.Release()
	err := iter.Error()
	fmt.Printf("Error: %v\n\n\n", err)
}

Hay que ejecutarlo una vez que se paren los nodos geth (pues si no los archivos estarán en uso y no se pordrá)

Blockchain Ethereum privada

Lanzamos la blockchain privada, esta vez desde un mismo pc, con windows, con dos instancias del cliente geth. Cada una de ellas será un nodo validador. La primera será también un bootnode a la que se conectará la segunda.

Al estar en el mismo pc, es importante recordar que cada instancia ha de especificar directorios distintos, puertos distintos, una de ellas no pordrá tener habilitado el ipc (por lo que no nos podremos conectar a ella por este mecanismo), y en cada carpeta keystore que se crea después de la inicialización con el bloque génesis, habrá que poner el archivo creado con la cuenta (ver post Ethereum Blockchain privada)

El bloque génesis lo creamos con puppet, con dos nodos validadores (para que la blochain funcione con los dos que vamos a ejecutar, son necesarios que se ejecuten N/2+1, siendo N el número de validadores definido en el bloque génesis). La primera cuenta será la que tiene fondos:

{
    "config": {
      "chainId": 777,
      "homesteadBlock": 0,
      "eip150Block": 0,
      "eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
      "eip155Block": 0,
      "eip158Block": 0,
      "byzantiumBlock": 0,
      "constantinopleBlock": 0,
      "petersburgBlock": 0,
      "istanbulBlock": 0,
      "clique": {
        "period": 30,
        "epoch": 30000
      }
    },
    "nonce": "0x0",
    "timestamp": "0x63ad7b9c",
    "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000347438f5a3d26e224ca448b05e932566923ece0d598b65946815ae60c68ec34d747bae6d166902870000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "gasLimit": "0x47b760",
    "difficulty": "0x1",
    "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
    "coinbase": "0x0000000000000000000000000000000000000000",
    "alloc": {
      "598b65946815ae60c68ec34d747bae6d16690287": {
        "balance": "0x300000000000000000000000000000000000000000000000000000000000000"
      }
    },
    "number": "0x0",
    "gasUsed": "0x0",
    "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
    "baseFeePerGas": null
  }

Después justo de inicializar la blockchain (con el comando geth init –datadir Nombredirectorio genesis.json) se crea ya la base de datos leveldb, que podemos analizar, pero tiene poco contenido, solo el boque 0 y los datos del génesis. Por ello, ejecutamos ambos nodos. Inmediatamente se mina el bloque 1, con prácticamente los mismos datos que el bloque 0.

Los comandos que vamos a usar son (en dos instancias distintas de la línea de comandos o shell):

Primer nodo:

>geth.exe init --datadir eth_network  genesis.json

(copiar en la carpeta keystore creada el archivo generado en la creación de la cuenta)

>geth.exe --datadir eth_network --networkid 888 --port 30304 --nat extip:127.0.0.1  --unlock 0x598b65946815ae60c68ec34d747bae6d16690287 --mine --nodiscover --syncmode full

Una vez lanzado, desde otra ventana de comandos nos conectamos a la consola de geth con el comando geth attach, y una vez en la consola, con el comando admin.nodeInfo tenemos el valor del enode para que otros nodos se conecte a éste (bootnode)

enode: "enode://2fc7faae575860b68924add776981084c562c5d6a55a5c92e8f1e669dc891974a702508abbbaf377b96a25c5f5c72fdaf1910af8d1fc902a3886ae66851f13d6@127.0.0.1:30304?discport=0",
  enr: "enr:-Jy4QDMnSAlDnHiz2USW1s6kXttGDU8oYjPBpbulhdmw5tMeQMlgXwev81q8GCHj68xa5Lsgpf5j-cG5lM8pJ1thl2-GAYWqiVLXg2V0aMfGhNHNc2OAgmlkgnY0gmlwhH8AAAGJc2VjcDI1NmsxoQIvx_quV1hgtokkrdd2mBCExWLF1qVaXJLo8eZp3IkZdIRzbmFwwIN0Y3CCdmA",
  id: "bc46f5f1fcdc0c691548aa042efc283113cc31f0167a25e773c4d735100ef20a",
  ip: "127.0.0.1",
  listenAddr: "[::]:30304",

Segundo nodo:

Ponemos otros puertos y nos sincronizamos con el nodo anterior, pasándole como parámetro bootnode la cadena enode obtenida.

>geth.exe init --datadir eth_network2  genesis.json

(copiar en la carpeta keystore creada el archivo generado en la creación de la cuenta)

>geth.exe --datadir eth_network2 --networkid 888 --port 30305  --authrpc.port 8552 --ipcdisable  --unlock 0x347438f5a3d26e224ca448b05e932566923ece0d –mine  --bootnodes "enode://2fc7faae575860b68924add776981084c562c5d6a55a5c92e8f1e669dc891974a702508abbbaf377b96a25c5f5c72fdaf1910af8d1fc902a3886ae66851f13d6@127.0.0.1:30304?discport=30304" --syncmode full

Con esto tenemos ya la blockchain funcionando

Las dos consolas de comandos cada una con un nodo de la blockchain Ethereum privada

Nos conectamos con una consola nos conectamos al primer nodo (en otra consola de comandos de windows, ejecutamos get attach, para enviar la transacción directamente desde ella (con las instrucciones de https://geth.ethereum.org/docs/getting-started, y el comando eth.sendTransaction, lo haremos desde la cuenta del propio nodo 1, que es la que tiene fondos, tal como se indicó en el bloque génesis, a otra cuenta que hemos creado:

Paramos ambos nodos, en ese momento se han minado/validado 4 bloques

La consulta de los bloques 0 y 1 desde la consola geth attach muestra los valores:

Consideraciones previas para interpretar los key/values de levelDB

Para poder interpretar correctamente lo que se almacena en la base de datos leveldb hay que tener disponibles algunas herramientas y consideraciones:

La función schema.go (en core/rawdb https://github.com/ethereum/go-ethereum/blob/master/core/rawdb/schema.go) proporciona los dats necesarios para ver de qué tipo son los datos que se almacenan en una determinada clave (key) de leveldb. Copio algunos interesantes:

headerNumberPrefix = []byte("H") // headerNumberPrefix + hash -> num (uint64 big endian)
blockBodyPrefix     = []byte("b") // blockBodyPrefix + num (uint64 big endian) + hash -> block body
headerPrefix       = []byte("h") // headerPrefix + num (uint64 big endian) + hash -> header
headerTDSuffix     = []byte("t") // headerPrefix + num (uint64 big endian) + hash + headerTDSuffix -> td -> total difficulty
headerHashSuffix   = []byte("n") // headerPrefix + num (uint64 big endian) + headerHashSuffix -> hash
blockReceiptsPrefix = []byte("r") // blockReceiptsPrefix + num (uint64 big endian) + hash -> block receipts
SnapshotAccountPrefix = []byte("a") // SnapshotAccountPrefix + account hash -> account trie value

La manera de interpretarlo es que una clave estará formada por un conjunto de bytes, en donde se añaden prefijos y sufijos junto con otros valores numéricos o hashes. Algunos ejemplos:

headerNumberPrefix = [ ]byte(«H») // headerNumberPrefix + hash -> num (uint64 big endian)Key: caracter «H»en hexadecimal (48) seguido del hash del bloque

Valor: número del bloque (entero de 64 bits)
blockBodyPrefix     = [ ]byte(«b») // blockBodyPrefix + num (uint64 big endian) + hash -> block bodyKey: caracter «b«en hexadecimal (62) seguido del número del bloque (64bits) y del hash del bloque

Valor: Body del bloque, es decir las transacciones, JSON codificado en RLP.
Si no hay transacciones, el body es vacío y su valor es c2c0c0
headerPrefix       = [ ]byte(«h») // headerPrefix + num (uint64 big endian) + hash -> headerKey: caracter «h«en hexadecimal (68) seguido del número de bloque (64bits) y del hash del bloque

Valor: cabecera del bloque, JSON codificado en RLP.
etc….
Ejemplo de interpretación de los key en la base de datos levelDB

Hay que tener en cuenta también algunos valores por defecto que representan valores nulos de algunas estrcturas (tal como el valor c2c0c0 representa un bloque con su body vacío), tal como se describe en https://github.com/ethereumjs/ethereumjs-util/blob/master/docs/modules/_constants_.md, y que aparecen como valores p.ej en valores nulos de hashes (tries vacíos) y otras estructuras (código en una cuenta)

KECCAK256_NULL_S:string= c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470″

KECCAK256_RLP_ARRAY_S: string = «1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347»

KECCAK256_RLP_S: string = «56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421»

Volcado de la base de datos LevelDB

Tras ejecutar el script de volcado de la base de datos del inicio de este post, el resultado es el siguiente:

Key: 0d692846be00fad6ce369880979465ca0dde7d558e015e29512b2391c89aba04 
Value: f889a02064245df25118b8c2b07f8d67fab2bcdc6ea50a250f89e4aacf5cab94b3bc8cb866f86401a002fffffffffffffffffffffffffffffffffffffffffffffffffe3940ad9cc000a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470


Key: 446174616261736556657273696f6e 
Value: 08


Key: 4812dcff3e2d0035dc5e80cfb658f4667ebced45a923a6936219d42acdd026fe79 
Value: 0000000000000000


Key: 482873fbd012b15c5328095e21e862f4db9635df93198ebf948f78ac7b79493e55 
Value: 0000000000000003


Key: 482e8efa23bfb26587ed601151bd147fb36a04267ac0e6ace19e82c3167b525b1a 
Value: 0000000000000001


Key: 48abab27aab0b4d02300a9b19aaebd6727c3b332bae4120afee10deb78307963ae 
Value: 0000000000000004


Key: 48b7cc03d74cb474a6c9520455f1ca38701d558b26b92ea45c8269b583037ed934 
Value: 0000000000000002


Key: 4c617374426c6f636b 
Value: abab27aab0b4d02300a9b19aaebd6727c3b332bae4120afee10deb78307963ae


Key: 4c61737446617374 
Value: abab27aab0b4d02300a9b19aaebd6727c3b332bae4120afee10deb78307963ae


Key: 4c617374486561646572 
Value: abab27aab0b4d02300a9b19aaebd6727c3b332bae4120afee10deb78307963ae


Key: 4fe4d34c34f613ad7a8e483a822dd59644c200b7cc169201075bfd07354da963 
Value: f8518080a00d692846be00fad6ce369880979465ca0dde7d558e015e29512b2391c89aba048080808080a0ab9b0f2548412ee47ba41d4a627a7510112cbfd247443fc983ba0969eee375488080808080808080


Key: 536e617073686f7447656e657261746f72 
Value: c6800180808080


Key: 536e617073686f744a6f75726e616c 
Value: 80a0f9accd7beef58d97e3e7bd4addd9d01ecedf4c092cc532e78f941f4a586ec732a0f5f7c51cf2257a9285226f246617431723658c966aa9721fe9b361b265927817c0f878f847a0c264245df25118b8c2b07f8d67fab2bcdc6ea50a250f89e4aacf5cab94b3bc8ca5e401a002fffffffffffffffffffffffffffffffffffffffffffffffffe3940ad9cc0008080eea0c81147d14a9d46c37d277394cc51442ea6f10bfe350b8f0a4f7a8049172432a28ccb808701c6bf526340008080c0


Key: 536e617073686f74526f6f74 
Value: f9accd7beef58d97e3e7bd4addd9d01ecedf4c092cc532e78f941f4a586ec732


Key: 5472616e73616374696f6e496e6465785461696c 
Value: 0000000000000000


Key: 61c264245df25118b8c2b07f8d67fab2bcdc6ea50a250f89e4aacf5cab94b3bc8c 
Value: e480a003000000000000000000000000000000000000000000000000000000000000008080


Key: 62000000000000000012dcff3e2d0035dc5e80cfb658f4667ebced45a923a6936219d42acdd026fe79 
Value: c2c0c0


Key: 6200000000000000012e8efa23bfb26587ed601151bd147fb36a04267ac0e6ace19e82c3167b525b1a 
Value: c2c0c0


Key: 620000000000000002b7cc03d74cb474a6c9520455f1ca38701d558b26b92ea45c8269b583037ed934 
Value: c2c0c0


Key: 6200000000000000032873fbd012b15c5328095e21e862f4db9635df93198ebf948f78ac7b79493e55 
Value: f871f86ef86c80843b9aca0082520894d00411828e14f75b0bada0d5173974e9451586158701c6bf5263400080820635a02c357ff0b2f42c9fb0bbe59e06a5a2867814e3d181c75991e2ade4109fa8f534a05805a20eb4c9e84ac72feb333cb0edd1e28e18e15018ac14b3219251e049ea2bc0


Key: 620000000000000004abab27aab0b4d02300a9b19aaebd6727c3b332bae4120afee10deb78307963ae 
Value: c2c0c0


Key: 636c697175652d12dcff3e2d0035dc5e80cfb658f4667ebced45a923a6936219d42acdd026fe79 
Value: 7b226e756d626572223a302c2268617368223a22307831326463666633653264303033356463356538306366623635386634363637656263656434356139323361363933363231396434326163646430323666653739222c227369676e657273223a7b22307833343734333866356133643236653232346361343438623035653933323536363932336563653064223a7b7d2c22307835393862363539343638313561653630633638656333346437343762616536643136363930323837223a7b7d7d2c22726563656e7473223a7b7d2c22766f746573223a6e756c6c2c2274616c6c79223a7b7d7d


Key: 657468657265756d2d636f6e6669672d12dcff3e2d0035dc5e80cfb658f4667ebced45a923a6936219d42acdd026fe79 
Value: 7b22636861696e4964223a3737372c22686f6d657374656164426c6f636b223a302c22656970313530426c6f636b223a302c2265697031353048617368223a22307830303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030222c22656970313535426c6f636b223a302c22656970313538426c6f636b223a302c2262797a616e7469756d426c6f636b223a302c22636f6e7374616e74696e6f706c65426c6f636b223a302c2270657465727362757267426c6f636b223a302c22697374616e62756c426c6f636b223a302c22636c69717565223a7b22706572696f64223a33302c2265706f6368223a33303030307d7d


Key: 657468657265756d2d67656e657369732df9accd7beef58d97e3e7bd4addd9d01ecedf4c092cc532e78f941f4a586ec732 
Value: 7b22307835393862363539343638313561653630633638656333346437343762616536643136363930323837223a7b2262616c616e6365223a223078333030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030227d7d


Key: 68000000000000000012dcff3e2d0035dc5e80cfb658f4667ebced45a923a6936219d42acdd026fe79 
Value: f9027ea00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a0f9accd7beef58d97e3e7bd4addd9d01ecedf4c092cc532e78f941f4a586ec732a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001808347b760808463ad7b9cb8890000000000000000000000000000000000000000000000000000000000000000347438f5a3d26e224ca448b05e932566923ece0d598b65946815ae60c68ec34d747bae6d166902870000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000880000000000000000


Key: 68000000000000000012dcff3e2d0035dc5e80cfb658f4667ebced45a923a6936219d42acdd026fe7974 
Value: 01


Key: 6800000000000000006e 
Value: 12dcff3e2d0035dc5e80cfb658f4667ebced45a923a6936219d42acdd026fe79


Key: 6800000000000000012e8efa23bfb26587ed601151bd147fb36a04267ac0e6ace19e82c3167b525b1a 
Value: f90256a012dcff3e2d0035dc5e80cfb658f4667ebced45a923a6936219d42acdd026fe79a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a0f9accd7beef58d97e3e7bd4addd9d01ecedf4c092cc532e78f941f4a586ec732a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002018347c94c808463c14a60b861da83010b00846765746888676f312e31392e358777696e646f77730000000000d0d733242f57f39f9d93468c7c0c4d37efb68ad6b9da585e2eaa254838aaaa1e74617dcbd70340a3e0f7ce1fb0b7b1b0d66a528f7c52c74135c41b5bfff78e7c01a00000000000000000000000000000000000000000000000000000000000000000880000000000000000


Key: 6800000000000000012e8efa23bfb26587ed601151bd147fb36a04267ac0e6ace19e82c3167b525b1a74 
Value: 03


Key: 6800000000000000016e 
Value: 2e8efa23bfb26587ed601151bd147fb36a04267ac0e6ace19e82c3167b525b1a


Key: 6800000000000000026e 
Value: b7cc03d74cb474a6c9520455f1ca38701d558b26b92ea45c8269b583037ed934


Key: 680000000000000002b7cc03d74cb474a6c9520455f1ca38701d558b26b92ea45c8269b583037ed934 
Value: f90256a02e8efa23bfb26587ed601151bd147fb36a04267ac0e6ace19e82c3167b525b1aa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a0f9accd7beef58d97e3e7bd4addd9d01ecedf4c092cc532e78f941f4a586ec732a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002028347db3d808463c14d16b861da83010b00846765746888676f312e31392e358777696e646f77730000000000f0b7924e05a2c30d856032f41ba0bb46b36d0393c64bfa3e94626c78fcc0ec060064663ee6ba97b04730ac1bfc68a6f6629630e84c611139e3166f7f822b392a01a00000000000000000000000000000000000000000000000000000000000000000880000000000000000


Key: 680000000000000002b7cc03d74cb474a6c9520455f1ca38701d558b26b92ea45c8269b583037ed93474 
Value: 05


Key: 6800000000000000032873fbd012b15c5328095e21e862f4db9635df93198ebf948f78ac7b79493e55 
Value: f90258a0b7cc03d74cb474a6c9520455f1ca38701d558b26b92ea45c8269b583037ed934a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a0f5f7c51cf2257a9285226f246617431723658c966aa9721fe9b361b265927817a011face298d7b035c89004a760d514e7b38741b185ffc6a143ffc17683e503f0ca0056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002038347ed328252088463c14d34b861da83010b00846765746888676f312e31392e358777696e646f777300000000001a89b4d2449ede84a141d49583e313c4aaf9bf636feea18a70e27c43a5e4e2cf59525e00b1768bf0e773cc89570fe44816d8517098bf78ccc01594845c2bf5bb01a00000000000000000000000000000000000000000000000000000000000000000880000000000000000


Key: 6800000000000000032873fbd012b15c5328095e21e862f4db9635df93198ebf948f78ac7b79493e5574 
Value: 07


Key: 6800000000000000036e 
Value: 2873fbd012b15c5328095e21e862f4db9635df93198ebf948f78ac7b79493e55


Key: 6800000000000000046e 
Value: abab27aab0b4d02300a9b19aaebd6727c3b332bae4120afee10deb78307963ae


Key: 680000000000000004abab27aab0b4d02300a9b19aaebd6727c3b332bae4120afee10deb78307963ae 
Value: f90256a02873fbd012b15c5328095e21e862f4db9635df93198ebf948f78ac7b79493e55a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a0f5f7c51cf2257a9285226f246617431723658c966aa9721fe9b361b265927817a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002048347ff2c808463c14d52b861da83010b00846765746888676f312e31392e358777696e646f77730000000000e36fc4663e07fdedb73dc9e075d6e1a3d3f23de5ebe30332df636bc2904e33cd595f84f252dc1ea37d03adec66967e95718ba91d8e99df4c143008295c7dfb6101a00000000000000000000000000000000000000000000000000000000000000000880000000000000000


Key: 680000000000000004abab27aab0b4d02300a9b19aaebd6727c3b332bae4120afee10deb78307963ae74 
Value: 09


Key: 6c5ed5d536f42dfc640169d2b5ff630218a5483a2a2afaa05c2ca308bc284ed3be 
Value: 03


Key: 72000000000000000012dcff3e2d0035dc5e80cfb658f4667ebced45a923a6936219d42acdd026fe79 
Value: c0


Key: 7200000000000000012e8efa23bfb26587ed601151bd147fb36a04267ac0e6ace19e82c3167b525b1a 
Value: c0


Key: 720000000000000002b7cc03d74cb474a6c9520455f1ca38701d558b26b92ea45c8269b583037ed934 
Value: c0


Key: 7200000000000000032873fbd012b15c5328095e21e862f4db9635df93198ebf948f78ac7b79493e55 
Value: c6c501825208c0


Key: 720000000000000004abab27aab0b4d02300a9b19aaebd6727c3b332bae4120afee10deb78307963ae 
Value: c0


Key: 756e636c65616e2d73687574646f776e 
Value: c280c0


Key: ab9b0f2548412ee47ba41d4a627a7510112cbfd247443fc983ba0969eee37548 
Value: f870a0201147d14a9d46c37d277394cc51442ea6f10bfe350b8f0a4f7a8049172432a2b84df84b808701c6bf52634000a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470


Key: f5f7c51cf2257a9285226f246617431723658c966aa9721fe9b361b265927817 
Value: e21ca04fe4d34c34f613ad7a8e483a822dd59644c200b7cc169201075bfd07354da963


Key: f9accd7beef58d97e3e7bd4addd9d01ecedf4c092cc532e78f941f4a586ec732 
Value: f88aa120c264245df25118b8c2b07f8d67fab2bcdc6ea50a250f89e4aacf5cab94b3bc8cb866f86480a00300000000000000000000000000000000000000000000000000000000000000a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470


Error: <nil>

Interpretación de los datos key/value

El primer par de valores que aparece lo veremos después, pues está relacionado con datos posteriores.

El segundo key 446174616261736556657273696f6e resulta ser la cadena «Databaseversion«, con valor 8,

Los siguientes comienzan por 48, «H» en hexadecimal, que indican el número de bloque, el 48 va seguido del hash correspondiente de cada bloque:

Key: 48cba02c36542969234a59bb93262fd761ba4b8505410a9ed02895661a857783a1

Value: 0000000000000001

Luego hay varias claves con valores predefinidos (schema.go)

Key: 4c617374426c6f636b -> «LastBlock» (Hex->ASCII)
Value: abab27aab0b4d02300a9b19aaebd6727c3b332bae4120afee10deb78307963ae

Key: 4c61737446617374 -> «LastFast» (Hex->ASCII)
Value: abab27aab0b4d02300a9b19aaebd6727c3b332bae4120afee10deb78307963ae

Key: 4c617374486561646572-> «LastHeader»(Hex->ASCII)
Value: abab27aab0b4d02300a9b19aaebd6727c3b332bae4120afee10deb78307963ae

El valor de todos ellos es el hash del bloque 4, que podemos verificar con la entrada:

Key: 6800000000000000046e 68 -> «h», número 000…004, 6e -> «n» (el valor es el hash según schema.go)
Value: abab27aab0b4d02300a9b19aaebd6727c3b332bae4120afee10deb78307963ae

Transacción

El cuerpo (body) de los bloques sin transacciones aparecen vacíos, la transacción aparece en el bloque 3, en los siguientes tenemos el prefijo 62 («b»), seguido de un número de 64 bits (8 pares de números) y el hash de cada bloque, que según schema.go proporciona el body del bloque, si es nulo el valor por defecto es c2c0c0 (separo los distintos grupos para mayor claridad)

Key: 62  00000000000000001  2dcff3e2d0035dc5e80cfb658f4667ebced45a923a6936219d42acdd026fe79
Value: c2c0c0
Key: 62  0000000000000001  2e8efa23bfb26587ed601151bd147fb36a04267ac0e6ace19e82c3167b525b1a
Value: c2c0c0
Key: 62  0000000000000002  b7cc03d74cb474a6c9520455f1ca38701d558b26b92ea45c8269b583037ed934
Value: c2c0c0
Key: 62  0000000000000003  2873fbd012b15c5328095e21e862f4db9635df93198ebf948f78ac7b79493e55
Value: f871f86ef86c80843b9aca0082520894d00411828e14f75b0bada0d5173974e9451586158701c6bf5263400080820635a02c357ff0b2f42c9fb0bbe59e06a5a2867814e3d181c75991e2ade4109fa8f534a05805a20eb4c9e84ac72feb333cb0edd1e28e18e15018ac14b3219251e049ea2bc0
Key: 62  0000000000000004  abab27aab0b4d02300a9b19aaebd6727c3b332bae4120afee10deb78307963ae
Value: c2c0c0

El valor de la transacción, en el body del bloque 3 hay que decodificarlo, está codificado en RLP

decodificar RLP
El valor decodificado es: [[["0x","0x3b9aca00","0x5208","0xd00411828e14f75b0bada0d5173974e945158615","0x01c6bf52634000","0x","0x0635","0x2c357ff0b2f42c9fb0bbe59e06a5a2867814e3d181c75991e2ade4109fa8f534","0x5805a20eb4c9e84ac72feb333cb0edd1e28e18e15018ac14b3219251e049ea2b"]],[]]

Que verificamos con los correspondientes valores que arroja la consola (leveldb almacena el nonce, 0x3b9aca00= 1000000000 gasPrice, 0x5208= 21000 gas , cuenta destino, 0x01c6bf52634000= 500000000000000 valor en wei, transaction index, v, r, s)

Block Header

Ahora vemos el valor del header del bloque 4, que es el último, prefijo 68 («h») , seguido del número de bloque y el hash

Key: 68 0000000000000004 abab27aab0b4d02300a9b19aaebd6727c3b332bae4120afee10deb78307963ae
Value: f90256a02873…………….00000

El valor está codificado en RLP, hay que decodificarlo, que porporciona el sigueinte JSON:

["0x2873fbd012b15c5328095e21e862f4db9635df93198ebf948f78ac7b79493e55","0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","0x0000000000000000000000000000000000000000","0xf5f7c51cf2257a9285226f246617431723658c966aa9721fe9b361b265927817","0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","0x02","0x04","0x47ff2c","0x","0x63c14d52","0xda83010b00846765746888676f312e31392e358777696e646f77730000000000e36fc4663e07fdedb73dc9e075d6e1a3d3f23de5ebe30332df636bc2904e33cd595f84f252dc1ea37d03adec66967e95718ba91d8e99df4c143008295c7dfb6101","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000"]

Los valores en el JSON siguen el orden de la siguiente figura

Estructura de datos de un bloque Ethereum

Y podemos comprobar todos los valores con los que proporciona la consola geth:

bloque 4

Es interesante darse cuenta de los valores nulos (vistos en el apartado de consideraciones previas) para receiptsRoot, sha3Uncles y transactionsRoot, pues en este bloque no hay transacciones, que en comparación con el bloque 3, donde existía la transacción:

Valores para el bloque 3

StateRoot

El state root nos permite acceder al estado de las cuentas. Vemos como cambia en el boque 3 donde se efectuó la transacción, y permanece hasta que vuelvan a ocurrir transacciones que modifiquen el estado

Valores de stateRoot en los bloques

Para poder interpretar los resultados es necesario conocer los tipos de nodos que existen, que además se identifican con un prefijo, que cambia de valor dependiendo del tipo de nodo y de la longitud (par o impar) del hash restante en el recorrido del trie/árbol, tal como se especifica en https://ethereum.org/en/developers/docs/data-structures-and-encoding/patricia-merkle-trie/#specification, (y otras tantas referencias que incluyen el esquema con la estructura de datos,ver p.ej. https://medium.com/shyft-network/understanding-trie-databases-in-ethereum-9f03d2c3325d)

Hay tres tipos de nodos: extension node, branch node (rama) y leaf node (hoja). El nodo branch o rama tiene 17 campos, los nodos extension y hoja tienen 2.. La forma de identificar los nodos extension y hoja es con ese prefijo mencionado, p. ej para un par de hashes XYYY, YYYY donde se comparte un nibble (X),

prefijo
(hexadecimal)
Tipo de nodolongitud del hash restante
00Extension Nodepar YYYY
1XExtension Nodeimpar YYY
20Leaf Nodepar YYYY
3XLeaf Nodeimpar YYY
prefijos para los nodos del State Trie

En nuesto caso, el valor del stateRoot f5f7c51cf2257a9285226f246617431723658c966aa9721fe9b361b265927817 aparece en la leveldb:

Key: f5f7c51cf2257a9285226f246617431723658c966aa9721fe9b361b265927817
Value: e21ca04fe4d34c34f613ad7a8e483a822dd59644c200b7cc169201075bfd07354da963

Está en RLP, y decodificando, se obtiene el JSON:

["0x1c","0x4fe4d34c34f613ad7a8e483a822dd59644c200b7cc169201075bfd07354da963"]

Representa un extension node del State Trie, el primer campo 0x1c, nos indica que es un extension node con un número impar de nibbles y los nibble compartidos, en este caso, es solo «c«. Es decir, que la letra «c»es un nodo del trie compartido por nodos hijos. El segundo campo lo volvemos a encontrar en levelb:

Key: 4fe4d34c34f613ad7a8e483a822dd59644c200b7cc169201075bfd07354da963
Value: f8518080a00d692846be00fad6ce369880979465ca0dde7d558e015e29512b2391c89aba048080808080a0ab9b0f2548412ee47ba41d4a627a7510112cbfd247443fc983ba0969eee375488080808080808080

Cuyo valor está de nuevo codificado en RLP, y decodificado proporciona el JSON:

["0x","0x","0x0d692846be00fad6ce369880979465ca0dde7d558e015e29512b2391c89aba04","0x","0x","0x","0x","0x","0xab9b0f2548412ee47ba41d4a627a7510112cbfd247443fc983ba0969eee37548","0x","0x","0x","0x","0x","0x","0x","0x"]

Este es un nodo rama (Branch Node) con 17 campos, cada uno representa una key en la leveldb. Hay dos campos no nulos, el que ocupa la posición 2 y el que ocupa la posición 8 (esto es importante a la hora de recorrer el trie) El primer valor es justo el primero que encontrábamos en el volcado de la base de datos, que dejamos para este momento:

Key: 0d692846be00fad6ce369880979465ca0dde7d558e015e29512b2391c89aba04
Value: f889a02064..........d8045d85a470

El valor, codificado en RLP proporciona el siguiente JSON decodificado:

["0x2064245df25118b8c2b07f8d67fab2bcdc6ea50a250f89e4aacf5cab94b3bc8c","0xf86401a002fffffffffffffffffffffffffffffffffffffffffffffffffe3940ad9cc000a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"]

de nuevo tenemos dos campos, el primero 0x20 (prefijo de nodo hoja con número par de bytes en el resto del hash, según hemos visto en la tabla de prefijos y tipos de nodos) seguido del hash de la cuenta emisora de la transacción, sin contar con la letra «c» que ya teníamos en el nibble del extension node y el ‘2’ de la posición en el nodo hoja (que permiten construir la ruta completa en el trie):

 primera cuenta: Keccak256(598b65946815ae60c68ec34d747bae6d16690287) = c264245df25118b8c2b07f8d67fab2bcdc6ea50a250f89e4aacf5cab94b3bc8c

el segundo campo está codificado en RLP, y al decodificar muestra el estado de la primera cuenta:

["0x01","0x02fffffffffffffffffffffffffffffffffffffffffffffffffe3940ad9cc000","0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"]

Donde está el nonce (ya hay una transacción efectuada), el balance, el storageRoot y el codehash, estos dos últimos con los valores de hashes nulos por defecto, ya que esta cuenta es EOA y no un contrato. (Nota: el balance es un número inmenso, ya que en el bloque génesis así se indicó, su conversión de hexadecimal a decimal coincide con el valor de la consola)

Balance de la cuenta exagerado, pero es el correspondiente a la inicialización en el bloque génesis

El segundo valor del nodo extensión del stateRoot también lo encontramos en la leveldb:

Key: ab9b0f2548412ee47ba41d4a627a7510112cbfd247443fc983ba0969eee37548
Value: f870a0201147.........fad8045d85a470

Decodificando el valor (RLP) obtenemos el siguiente JSON:

["0x201147d14a9d46c37d277394cc51442ea6f10bfe350b8f0a4f7a8049172432a2","0xf84b808701c6bf52634000a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"]

Análogamente al caso anterior tenemos dos campos, el primero 0x20 (de nuevo prefijo para nodo hoja con número par de bytes del hash restante) seguido del hash de la cuenta destino de la transacción, sin contar con la letra «c» que ya teníamos en el nibble del extension node y el ‘8’ que correspondía a su posición para calcular la ruta en el trie:

primera cuenta: Keccak256(d00411828e14f75b0bada0d5173974e945158615) = c81147d14a9d46c37d277394cc51442ea6f10bfe350b8f0a4f7a8049172432a2

el segundo campo está codificado en RLP, y al decodificar muestra el estado de la cuenta destino:

["0x","0x01c6bf52634000","0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"]

Tenemos el nonce=0 (esta cuenta no ha hecho transacciones), el balance 0x01c6bf52634000=500000000000000 wei, y de nuevo los valores nulos para el storageRoot y el codeHash.

state trie

Esquema del stateRoot (extension Node)

3 Comments

  1. Ethereum: leer LevelDB, Smart Contract y Storage Trie – Abraza la Web · viernes, 20 enero, 2023 Reply

    […] con los posts anteriores explorando la base de datos levelDB de Ethereum (ver Ethereum: explorando base de datos leveldb, donde se explora el State Trie y Ethereum Blockchain privada, Ethereum y LevelDB: Leer, Modificar […]

  2. Ethereum debug geth con Visual Studio Code – Abraza la Web · miércoles, 29 marzo, 2023 Reply

    […] (NodeJS, Git, gcc…), tal como se describe en post anteriores (Ethereum Blockchain privada, Ethereum: explorando base de datos leveldb), podemos debuggear el código de geth si lo omdificamos o para entender su […]

  3. Ethereum: Transaction Trie, Receipt Trie, eventos, logs en la base de datos levelDB, transacciones internas – Abraza la Web · domingo, 7 enero, 2024 Reply

    […] En algunas entradas anteriores hemos visto cómo leer la base de datos levelDB que un nodo Ethereum almacena (en su disco) e interpretado el State Trie (cuentas) y el Storage Trie (almacenamiento de los smart contracts), https://abrazalaweb.net/2023/01/ethereum-leer-leveldb-smart-contract-y-storage-trie/, https://abrazalaweb.net/2023/01/ethereum-explorando-base-de-datos-leveldb/. […]

Leave a Reply

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.