Ethereum y LevelDB: Leer, Modificar y Compilar Geth (II)

En la entrada anterior Ethereum y LevelDB: Leer, Modificar y Compilar Geth se trataba de modificar la base de datos LevelDB local de un nodo Ethereum, eliminando los datos de una transacción que ocurría en un bloque. Tras ello, un segundo nodo se inicializaba con el bloque génesis y se trataba de sincronizar al anterior, ocasionando, evidentemente, bastantes errores en cuanto a la comprobación de los bloques que se iba descargando. La idea era poder tratar de ir evitando cada error y así poder sincronizar el segundo nodo a una blockchain en la que se han eliminado transacciones (o cualquier otra modificación posible). En esa entrada anterior dejamos las cosas en el error ocasionado porque el segundo nodo no encontrada en su State Trie un nodo referenciado en el bloque siguiente al que se produce la transacción.

Continuamos ahora, aunque se ha modificado teniendo en cuenta las últimas entradas relativas a Ethereum y geth. Ahora la transacción, que se ha borrado en el nodo 1, ocurre en el bloque 5.

Error: falta un nodo del State Trie en el Nodo 2

Una manera para resolver la inconsistencia es justo después de la inicialización del Nodo 2 con el bloque génesis, modificar «offchain» su base de datos LevelDB y añadir los nodos del state trie que faltan (en general, habría que añadir todo el state trie del Nodo 1). Es una manera muy tirivial y casi «absurda», pero podría incluirse en el mecanismo de consenso, e irse haciendo durante el proceso de sincronización, para así poder tener una blockchain donde se pueden borrar cosas.

(NOTA: al modificar la base de datos levelDB del Nodo 1 en un determinado momento, y re-arrancar el nodo partiendo de esa base de datos modificada, no se produce ninguna verificación por parte de geth, y el Nodo 1 prosigue donde lo dejó, a pesar de, en este caso, tener el bloque 5 donde se ha eliminado el contenido blockbody, borrando la transacción)

(NOTA: un cliente ligero «light node», al descargarse las cabeceras de los bloques, se advierte este cambio del blockbody y no genera errores, ver al final)

Entonces los pasos para esta prueba son:

  1. Lanzar el Nodo 1 (donde previamente se modificó su base de datos local LevelDB para eliminar, en este caso, el contenido del bloque 5 con al transacción entre 2 cuentas). Usamos una instancia del ejecutable.
.\geth.exe --datadir eth_network --networkid 888 --port 30304 --http  --allow-insecure-unlock --http.corsdomain "*" --nat extip:127.0.0.1 --unlock 0x598b65946815ae60c68ec34d747bae6d16690287 --mine --nodiscover --syncmode full
  1. Inicializar el Nodo 2 con el bloque génesis (previamente borrar cualquier rastro de su base de datos local, que estará en el directorio correspondiente y carpeta geth (en este caso ./eth_network2/geth
./geth.exe init --datadir eth_network2 aepdeth_genesis.json
  1. Modificar la base de datos local LevelDB del Nodo 2 para añadir los nodos del State Trie que no va a encontrar (obtenidos en un proceso de depuración previo, en donde hay que tener en cuenta transacciones y receipts), pero que coincidirán con los faltantes en la sincronización de bloques cuando se producen transacciones). Para ello usamos este pequeño script en go (al que llamamos por ejemplo «writeLeveldb.go» y ejecutamos con el comando:
go run .\writeLeveldb.go
package main

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

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

	 
	fmt.Printf("\n=======ESCRIBIR levelDB\n")
	newKey :="da73a3004622ded0dd20bb0b1b729fa96a65f76b9f9f04877b9356045c724319"
	newValue :="e21ca0a635ca34f999df2deb14d6e7e10eee696e334880d6483d5338319efafd7cc44e"
	newHexKey, _ := hex.DecodeString(newKey)
	newHexValue, _ := hex.DecodeString(newValue)
	err := db.Put(newHexKey,newHexValue,nil)
	fmt.Printf("Insertado 1: Resultado error= %v\n",err)
	
	newKey= "a635ca34f999df2deb14d6e7e10eee696e334880d6483d5338319efafd7cc44e"
	newValue= "f8518080a021dc63cbdd41e1f68b7f355d7a4588defcefaf4fb9e32b6f35c1306f5e9a398b8080808080a00f39018e386a5a8733e5bb4dfaebce90aa692c6835322ea24ad1c340cc863e6c8080808080808080"
	newHexKey, _ = hex.DecodeString(newKey)
	newHexValue, _ = hex.DecodeString(newValue)
	err = db.Put(newHexKey,newHexValue,nil)
	fmt.Printf("Insertado 2: Resultado error= %v\n",err)
	
	newKey= "21dc63cbdd41e1f68b7f355d7a4588defcefaf4fb9e32b6f35c1306f5e9a398b"
	newValue= "f872a02064245df25118b8c2b07f8d67fab2bcdc6ea50a250f89e4aacf5cab94b3bc8cb84ff84d018902b5d1eb9d79a78000a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
	newHexKey, _ = hex.DecodeString(newKey)
	newHexValue, _ = hex.DecodeString(newValue)
	err = db.Put(newHexKey,newHexValue,nil)
	fmt.Printf("Insertado 3: Resultado error= %v\n",err)
	
	newKey="0f39018e386a5a8733e5bb4dfaebce90aa692c6835322ea24ad1c340cc863e6c"
	newValue= "f870a0201147d14a9d46c37d277394cc51442ea6f10bfe350b8f0a4f7a8049172432a2b84df84b808711c37937e08000a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
	newHexKey, _ = hex.DecodeString(newKey)
	newHexValue, _ = hex.DecodeString(newValue)
	err = db.Put(newHexKey,newHexValue,nil)
	fmt.Printf("Insertado 4: Resultado error= %v\n",err)

}
  1. Arrancar ahora el Nodo 2 para que se sincronice con el Nodo 1 (ponemos un alto nivel de log/ verbosity)
./geth.exe --datadir eth_network2 --networkid 888 --port 30305  --authrpc.port 8552 --ipcdisable --bootnodes "enode://e8021593007ca294dc66437fda9743f025610b210e4e0039a04596b7c981cbdc4dae556c900c530c8a032be06ec943309abf66b4585e3c5c72a702c5f3ddc4d8@127.0.0.1:30304?discport=30304" --syncmode full --verbosity 5

Con todo, el Nodo 2 finalmente sincroniza con el Nodo 1, con una blockchain modificada donde se ha eliminado una transacción.

Sincronización del Nodo 2 terminada, tras saltarse los errores y habiendo incluido previamente en su base de datos local LevelDB los nodos del State Trie del Nodo 1 que por razones obvias (Nodo 1 con datos de un bloque borrados) le faltan

Ahora nos conectamos a sendas consolas en cada nodo para comprobar los estados (que evidentemente son correctos)

Consola en el Nodo 1
Consola en Nodo 2

Cliente Ligero

Si repetimos la prueba con el Nodo 2 como cliente ligero «light client», en donde la sincronización se efectúa únicamente con las cabeceras de los bloques, entonces no se entera del cambio en el cuerpo del bloque y no se provoca ningún error.

El Nodo 1 ha de configurarse para que responda a clientes ligeros (light server, según la documentación oficial https://geth.ethereum.org/docs/fundamentals/les), y el Nodo 2 ha de configurarse en modo sincronización light:

Nodo 1:

Nodo 1:
.\geth.exe --datadir eth_network --networkid 888 --port 30304 --http  --allow-insecure-unlock --http.corsdomain "*" --nat extip:127.0.0.1 --unlock 0x598b65946815ae60c68ec34d747bae6d16690287 --mine --nodiscover --syncmode full --light.serve 90 --txlookuplimit 0

Nodo 2:
 .\gethDebug.exe  --datadir eth_network2 --networkid 888 --port 30305  --authrpc.port 8552 --ipcdisable --bootnodes "enode://e8021593007ca294dc66437fda9743f025610b210e4e0039a04596b7c981cbdc4dae556c900c530c8a032be06ec943309abf66b4585e3c5c72a702c5f3ddc4d8@127.0.0.1:30304?discport=30304" --syncmode light  --verbosity 5

No obstante, tal como se indica en la documentación de Ethereum (enlace anterior), no es fácil que un cliente ligero encuentre el peer para sincronizarse, y así ocurre si lanzamos los comandos anteriores. Por tanto, hay que añadirle al Nodo 1 como peer el Nodo 2, para ello, mientras ambos Nodos están ejecutándose, conectamos una consola geth al Nodo 1 (geth attach) y añadimos en Nodo 2 con su valor enode (que obtenemos de la información en la ejecución del Nodo 2:

Consola en el Nodo 1:
 admin.addPeer("enode://72a0d335e613628d6fae46924426b0e6e73d166ba0b3fa486029fa48cde0f04d0c97d92b95589561113913f0ec62ca84468405e2395641fc8914aaace3660e4b@127.0.0.1:30305")
true
La ejecución del Nodo 2 aporta la información de su endpoint enode

Comprobamos que efectivamente la sincronización en el Nodo 2 ocurre de manera satisfactoria y sin ningún error:

Tras añadir el peer mediante la consola del Nodo 1, el Nodo 2 comienzala sincronización
Sincronización del nodo ligero completada sin error

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.