From cc8f62089e25dc557507987f34c0305c679a4e10 Mon Sep 17 00:00:00 2001 From: Gui Iribarren Date: Tue, 30 Apr 2024 11:50:11 +0200 Subject: [PATCH] refactor genesis package * don't pass genesisBytes as arg on SetNode, NewVochain, newTendermint, etc * notably, now cometbft has it's own datadir separate from our state, allowing to wipe cometbft data without wiping our state. * drop genesis.AutoUpdateGenesis in favor of a new flag AutoWipeDataDir * add AutoWipeCometBFT flag * move SeedNodes from genesis into config package (DefaultSeedNodes) * add config consts: DefaultCometBFTPath and DefaultGenesisPath * testsuite: use genesis.go instead of hardcoded genesis.json this increases the code coverage of testsuite and helps developing refactors of genesis.go * now testsuite network is `test` (instead of `dev`) * app.genesisDoc is now populated during newTendermint, instead of pulling from app.NodeClient.Genesis, since it's needed during Info() handshake * cometbft: removed redundant app.State.SetHeight(lastHeight) during Info() * cometbft: now Info() supports doing a chain bump, if genesis InitialHeight > lastHeight * genesis: remove copypasted types, use upstream comettypes directly * genesis: deduplicate ConsensusParams literals using DefaultBlockParams, etc * genesis: remove useless hardcoded StateSyncParams --- cmd/tools/vochaininspector/inspector.go | 4 +- config/config.go | 4 + config/defaults.go | 25 ++ dockerfiles/testsuite/README.md | 2 - dockerfiles/testsuite/docker-compose.yml | 21 +- dockerfiles/testsuite/env.gateway0 | 3 +- dockerfiles/testsuite/env.gatewaySync | 3 +- dockerfiles/testsuite/env.miner0 | 2 +- dockerfiles/testsuite/env.miner1 | 2 +- dockerfiles/testsuite/env.miner2 | 2 +- dockerfiles/testsuite/env.miner3 | 2 +- dockerfiles/testsuite/env.seed | 2 +- dockerfiles/testsuite/genesis.json | 80 ----- service/vochain.go | 61 ++-- vochain/app.go | 11 +- vochain/appsetup.go | 9 +- vochain/apputils.go | 79 +++-- vochain/cometbft.go | 23 +- vochain/genesis/genesis.go | 380 +++++++++++++---------- vochain/genesis/types.go | 123 +------- vochain/start.go | 110 +++---- 21 files changed, 405 insertions(+), 543 deletions(-) delete mode 100755 dockerfiles/testsuite/genesis.json diff --git a/cmd/tools/vochaininspector/inspector.go b/cmd/tools/vochaininspector/inspector.go index 5215797be..8a312aa9e 100644 --- a/cmd/tools/vochaininspector/inspector.go +++ b/cmd/tools/vochaininspector/inspector.go @@ -20,7 +20,6 @@ import ( "go.vocdoni.io/dvote/statedb" "go.vocdoni.io/dvote/util" "go.vocdoni.io/dvote/vochain" - "go.vocdoni.io/dvote/vochain/genesis" "go.vocdoni.io/dvote/vochain/state" "go.vocdoni.io/dvote/vochain/vochaininfo" "go.vocdoni.io/proto/build/go/models" @@ -191,8 +190,7 @@ func newVochain(network, dataDir string) *vochain.BaseApplication { } log.Infof("external ip address %s", cfg.PublicAddr) // Create the vochain node - genesisBytes := genesis.Genesis[network].Genesis.Marshal() - return vochain.NewVochain(cfg, genesisBytes) + return vochain.NewVochain(cfg) } func listBlockVotes(_ int64, _ string) { diff --git a/config/config.go b/config/config.go index 1bec18521..7e37dab83 100644 --- a/config/config.go +++ b/config/config.go @@ -105,6 +105,10 @@ type VochainCfg struct { PrivValidatorListenAddr string // NoWaitSync if enabled the Vochain synchronization won't be blocking NoWaitSync bool + // AutoWipeDataDir enables wiping out the whole datadir when hardcoded genesis changes + AutoWipeDataDir bool + // AutoWipeCometBFT enables wiping out the cometbft datadir when hardcoded genesis changes + AutoWipeCometBFT bool // MempoolSize is the size of the mempool MempoolSize int // SkipPreviousOffchainData if enabled, the node will skip downloading the previous off-chain data to the current block diff --git a/config/defaults.go b/config/defaults.go index 511829456..bdb052494 100644 --- a/config/defaults.go +++ b/config/defaults.go @@ -3,4 +3,29 @@ package config // These consts are defaults used in VochainCfg const ( DefaultMinerTargetBlockTimeSeconds = 10 + DefaultCometBFTPath = "cometbft" + DefaultGenesisPath = DefaultCometBFTPath + "/config/genesis.json" ) + +// DefaultSeedNodes is a map indexed by network name +var DefaultSeedNodes = map[string][]string{ + // testsuite test network + "test": { + "3c3765494e758ae7baccb1f5b0661755302ddc47@seed:26656", + }, + // Development network + "dev": { + "7440a5b086e16620ce7b13198479016aa2b07988@seed.dev.vocdoni.net:26656", + }, + + // Staging network + "stage": { + "588133b8309363a2a852e853424251cd6e8c5330@seed.stg.vocdoni.net:26656", + }, + + // LTS production network + "lts": { + "32acbdcda649fbcd35775f1dd8653206d940eee4@seed1.lts.vocdoni.net:26656", + "02bfac9bd98bf25429d12edc50552cca5e975080@seed2.lts.vocdoni.net:26656", + }, +} diff --git a/dockerfiles/testsuite/README.md b/dockerfiles/testsuite/README.md index 30ef9fa33..53d221ade 100644 --- a/dockerfiles/testsuite/README.md +++ b/dockerfiles/testsuite/README.md @@ -21,8 +21,6 @@ the testnet is composed of: * four [miners](https://docs.vocdoni.io/architecture/services/vochain.html#miner) (aka [validator nodes](https://docs.tendermint.com/master/nodes/#validators) in tendermint jargon) * one [gateway](https://docs.vocdoni.io/architecture/components.html#gateway) -the `genesis.json` file lists the public keys of all the miners, since vochain is a Proof-of-Authority. - the seed node will serve to bootstrap the network: it'll just wait for incoming connections from other nodes, and provide them a list of peers which they can connect to. the miners will first connect to the seed node, get the list of peers, and connect to each other. when there are at least 3 miners online, they can reach consensus and start producing blocks. diff --git a/dockerfiles/testsuite/docker-compose.yml b/dockerfiles/testsuite/docker-compose.yml index d92a9a0a9..a32a1f22a 100644 --- a/dockerfiles/testsuite/docker-compose.yml +++ b/dockerfiles/testsuite/docker-compose.yml @@ -10,8 +10,7 @@ services: - blockchain volumes: - data-seed:/app/run/ - - ${COMPOSE_HOST_PATH:-.}/genesis.json:/app/misc/genesis.json - - /tmp/.vochain-zkCircuits/:/app/run/dev/zkCircuits/ + - /tmp/.vochain-zkCircuits/:/app/run/test/zkCircuits/ - gocoverage-seed:/app/run/gocoverage environment: - GOCOVERDIR=/app/run/gocoverage @@ -24,8 +23,7 @@ services: - blockchain volumes: - data-miner0:/app/run/ - - ${COMPOSE_HOST_PATH:-.}/genesis.json:/app/misc/genesis.json - - /tmp/.vochain-zkCircuits/:/app/run/dev/zkCircuits/ + - /tmp/.vochain-zkCircuits/:/app/run/test/zkCircuits/ - gocoverage-miner0:/app/run/gocoverage environment: - GOCOVERDIR=/app/run/gocoverage @@ -38,8 +36,7 @@ services: - blockchain volumes: - data-miner1:/app/run/ - - ${COMPOSE_HOST_PATH:-.}/genesis.json:/app/misc/genesis.json - - /tmp/.vochain-zkCircuits/:/app/run/dev/zkCircuits/ + - /tmp/.vochain-zkCircuits/:/app/run/test/zkCircuits/ - gocoverage-miner1:/app/run/gocoverage environment: - GOCOVERDIR=/app/run/gocoverage @@ -52,8 +49,7 @@ services: - blockchain volumes: - data-miner2:/app/run/ - - ${COMPOSE_HOST_PATH:-.}/genesis.json:/app/misc/genesis.json - - /tmp/.vochain-zkCircuits/:/app/run/dev/zkCircuits/ + - /tmp/.vochain-zkCircuits/:/app/run/test/zkCircuits/ - gocoverage-miner2:/app/run/gocoverage environment: - GOCOVERDIR=/app/run/gocoverage @@ -66,8 +62,7 @@ services: - blockchain volumes: - data-miner3:/app/run/ - - ${COMPOSE_HOST_PATH:-.}/genesis.json:/app/misc/genesis.json - - /tmp/.vochain-zkCircuits/:/app/run/dev/zkCircuits/ + - /tmp/.vochain-zkCircuits/:/app/run/test/zkCircuits/ - gocoverage-miner3:/app/run/gocoverage environment: - GOCOVERDIR=/app/run/gocoverage @@ -82,8 +77,7 @@ services: - blockchain volumes: - data-gateway0:/app/run/ - - ${COMPOSE_HOST_PATH:-.}/genesis.json:/app/misc/genesis.json - - /tmp/.vochain-zkCircuits/:/app/run/dev/zkCircuits/ + - /tmp/.vochain-zkCircuits/:/app/run/test/zkCircuits/ - gocoverage-gateway0:/app/run/gocoverage environment: - GOCOVERDIR=/app/run/gocoverage @@ -163,8 +157,7 @@ services: - blockchain volumes: - data-gatewaySync:/app/run/ - - ${COMPOSE_HOST_PATH:-.}/genesis.json:/app/misc/genesis.json - - /tmp/.vochain-zkCircuits/:/app/run/dev/zkCircuits/ + - /tmp/.vochain-zkCircuits/:/app/run/test/zkCircuits/ - gocoverage-gatewaySync:/app/run/gocoverage environment: - GOCOVERDIR=/app/run/gocoverage diff --git a/dockerfiles/testsuite/env.gateway0 b/dockerfiles/testsuite/env.gateway0 index 2b8432bf1..99e1555c8 100755 --- a/dockerfiles/testsuite/env.gateway0 +++ b/dockerfiles/testsuite/env.gateway0 @@ -1,5 +1,6 @@ VOCDONI_DATADIR=/app/run VOCDONI_MODE=gateway +VOCDONI_CHAIN=test VOCDONI_LOGLEVEL=debug VOCDONI_VOCHAIN_LOGLEVEL=error VOCDONI_DEV=True @@ -8,11 +9,9 @@ VOCDONI_ENABLERPC=True VOCDONI_ENABLEFAUCETWITHAMOUNT=100000 VOCDONI_VOCHAIN_PUBLICADDR=gateway0:26656 VOCDONI_VOCHAIN_SEEDS=3c3765494e758ae7baccb1f5b0661755302ddc47@seed:26656 -VOCDONI_VOCHAIN_GENESIS=/app/misc/genesis.json VOCDONI_VOCHAIN_NOWAITSYNC=True VOCDONI_METRICS_ENABLED=True VOCDONI_METRICS_REFRESHINTERVAL=5 -VOCDONI_CHAIN=dev VOCDONI_SIGNINGKEY=e0f1412b86d6ca9f2b318f1d243ef50be23d315a2e6c1c3035bc72d44c8b2f90 # 0x88a499cEf9D1330111b41360173967c9C1bf703f VOCDONI_ARCHIVEURL=none VOCDONI_VOCHAIN_STATESYNCENABLED=false diff --git a/dockerfiles/testsuite/env.gatewaySync b/dockerfiles/testsuite/env.gatewaySync index 49e01d5e6..6fc74085a 100644 --- a/dockerfiles/testsuite/env.gatewaySync +++ b/dockerfiles/testsuite/env.gatewaySync @@ -1,5 +1,6 @@ VOCDONI_DATADIR=/app/run VOCDONI_MODE=gateway +VOCDONI_CHAIN=test VOCDONI_LOGLEVEL=debug VOCDONI_VOCHAIN_LOGLEVEL=info VOCDONI_DEV=True @@ -8,11 +9,9 @@ VOCDONI_ENABLERPC=True VOCDONI_ENABLEFAUCETWITHAMOUNT=100000 VOCDONI_VOCHAIN_PUBLICADDR=gatewaySync:26656 VOCDONI_VOCHAIN_SEEDS=3c3765494e758ae7baccb1f5b0661755302ddc47@seed:26656 -VOCDONI_VOCHAIN_GENESIS=/app/misc/genesis.json VOCDONI_VOCHAIN_NOWAITSYNC=True VOCDONI_METRICS_ENABLED=True VOCDONI_METRICS_REFRESHINTERVAL=5 -VOCDONI_CHAIN=dev VOCDONI_SIGNINGKEY=f50be23d315a2e6c1c30e0f1412b86d6ca9f2b318f1d243e35bc72d44c8b2f90 VOCDONI_ARCHIVEURL=none VOCDONI_VOCHAIN_SNAPSHOTINTERVAL=3 diff --git a/dockerfiles/testsuite/env.miner0 b/dockerfiles/testsuite/env.miner0 index 5e3d449fe..a720a0263 100755 --- a/dockerfiles/testsuite/env.miner0 +++ b/dockerfiles/testsuite/env.miner0 @@ -1,11 +1,11 @@ VOCDONI_DATADIR=/app/run VOCDONI_MODE=miner +VOCDONI_CHAIN=test VOCDONI_LOGLEVEL=debug VOCDONI_VOCHAIN_LOGLEVEL=error VOCDONI_DEV=True VOCDONI_VOCHAIN_PUBLICADDR=miner0:26656 VOCDONI_VOCHAIN_SEEDS=3c3765494e758ae7baccb1f5b0661755302ddc47@seed:26656 -VOCDONI_VOCHAIN_GENESIS=/app/misc/genesis.json VOCDONI_VOCHAIN_MINERKEY=cda909c34901c137e12bb7d0afbcb9d1c8abc66f03862a42344b1f509d1ae4ce VOCDONI_METRICS_ENABLED=True VOCDONI_METRICS_REFRESHINTERVAL=5 diff --git a/dockerfiles/testsuite/env.miner1 b/dockerfiles/testsuite/env.miner1 index 3904bea35..2c25d2770 100755 --- a/dockerfiles/testsuite/env.miner1 +++ b/dockerfiles/testsuite/env.miner1 @@ -1,10 +1,10 @@ VOCDONI_DATADIR=/app/run VOCDONI_MODE=miner +VOCDONI_CHAIN=test VOCDONI_LOGLEVEL=info VOCDONI_DEV=True VOCDONI_VOCHAIN_PUBLICADDR=miner1:26656 VOCDONI_VOCHAIN_SEEDS=3c3765494e758ae7baccb1f5b0661755302ddc47@seed:26656 -VOCDONI_VOCHAIN_GENESIS=/app/misc/genesis.json VOCDONI_VOCHAIN_MINERKEY=d52a488fa1511a07778cc94ed9d8130fb255537783ea7c669f38292b4f53ac4f VOCDONI_METRICS_ENABLED=True VOCDONI_METRICS_REFRESHINTERVAL=5 diff --git a/dockerfiles/testsuite/env.miner2 b/dockerfiles/testsuite/env.miner2 index 8dc742042..3aa25579b 100755 --- a/dockerfiles/testsuite/env.miner2 +++ b/dockerfiles/testsuite/env.miner2 @@ -1,10 +1,10 @@ VOCDONI_DATADIR=/app/run VOCDONI_MODE=miner +VOCDONI_CHAIN=test VOCDONI_LOGLEVEL=info VOCDONI_DEV=True VOCDONI_VOCHAIN_PUBLICADDR=miner2:26656 VOCDONI_VOCHAIN_SEEDS=3c3765494e758ae7baccb1f5b0661755302ddc47@seed:26656 -VOCDONI_VOCHAIN_GENESIS=/app/misc/genesis.json VOCDONI_VOCHAIN_MINERKEY=e06976e5eaf3f147e12763eb140e3d5c2ed16a6fa747d787d8b92ca961fa7dc4 VOCDONI_METRICS_ENABLED=True VOCDONI_METRICS_REFRESHINTERVAL=5 diff --git a/dockerfiles/testsuite/env.miner3 b/dockerfiles/testsuite/env.miner3 index 44ff46f30..8fdaade9b 100755 --- a/dockerfiles/testsuite/env.miner3 +++ b/dockerfiles/testsuite/env.miner3 @@ -1,10 +1,10 @@ VOCDONI_DATADIR=/app/run VOCDONI_MODE=miner +VOCDONI_CHAIN=test VOCDONI_LOGLEVEL=info VOCDONI_DEV=True VOCDONI_VOCHAIN_PUBLICADDR=miner3:26656 VOCDONI_VOCHAIN_SEEDS=3c3765494e758ae7baccb1f5b0661755302ddc47@seed:26656 -VOCDONI_VOCHAIN_GENESIS=/app/misc/genesis.json VOCDONI_VOCHAIN_MINERKEY=b8d258559adee836a43e964badf541ec106cc68a01f989d3c3c9a030ae3945a6 VOCDONI_METRICS_ENABLED=True VOCDONI_METRICS_REFRESHINTERVAL=5 diff --git a/dockerfiles/testsuite/env.seed b/dockerfiles/testsuite/env.seed index 7482b0e65..d17736e19 100755 --- a/dockerfiles/testsuite/env.seed +++ b/dockerfiles/testsuite/env.seed @@ -1,10 +1,10 @@ VOCDONI_DATADIR=/app/run VOCDONI_MODE=seed +VOCDONI_CHAIN=test VOCDONI_LOGLEVEL=debug VOCDONI_DEV=True VOCDONI_VOCHAIN_PUBLICADDR=seed:26656 VOCDONI_VOCHAIN_LOGLEVEL=info -VOCDONI_VOCHAIN_GENESIS=/app/misc/genesis.json VOCDONI_VOCHAIN_NODEKEY=0x2060e20d1f0894d6b23901bce3f20f26107baf0335451ad75ef27b14e4fc56ae050a65ae3883c379b70d811d6e12db2fe1e3a5cf0cae4d03dbbbfebc68601bdd VOCDONI_METRICS_ENABLED=True VOCDONI_METRICS_REFRESHINTERVAL=5 diff --git a/dockerfiles/testsuite/genesis.json b/dockerfiles/testsuite/genesis.json deleted file mode 100755 index bc7ab735a..000000000 --- a/dockerfiles/testsuite/genesis.json +++ /dev/null @@ -1,80 +0,0 @@ -{ - "genesis_time": "2023-02-28T22:40:43.668920539Z", - "chain_id": "test-chain-1", - "initial_height": "0", - "consensus_params": { - "block": { - "max_bytes": "2097152", - "max_gas": "-1" - }, - "evidence": { - "max_age_num_blocks": "100000", - "max_age_duration": "10000", - "max_bytes": "1048576" - }, - "validator": { - "pub_key_types": [ - "secp256k1" - ] - }, - "version": { - "app_version": "0" - } - }, - "app_hash": "", - "app_state": { - "max_election_size": 1000000, - "network_capacity": 10000, - "validators": [ - { - "signer_address": "6bcc92be71d48cfe3f2f5042f157ad978f3e8117", - "consensus_pub_key": "038faa051e8a726597549bb057f1d296947bb54378443ec8fce030001ece678e14", - "power": 10, - "key_index": 1, - "name": "validator1" - }, - { - "signer_address": "032faef5d0f2c76bbd804215e822a5203e83385d", - "consensus_pub_key": "03cf8d0d1afa561e01145a275d1e41ed1a6d652361509a4c93dfc6488fdf5eca38", - "power": 10, - "key_index": 2, - "name": "validator2" - }, - { - "signer_address": "1f00b2ee957af530d44c8ceb1fecdd07c5702ad7", - "consensus_pub_key": "031802916d945239a39a9a8ee3e2eb3fb91ee324ccdfd73659f482e644892b796f", - "power": 10, - "key_index": 3, - "name": "validator3" - }, - { - "signer_address": "8992487e178fdcdc0dad95174e2a6d6229b3719c", - "consensus_pub_key": "02a790726e98978b0ca2cde3a09cbb1af1b298191f46e051b86bcb1854deb58478", - "power": 10, - "key_index": 4, - "name": "validator4" - } - ], - "accounts":[ - { - "address":"0x88a499cEf9D1330111b41360173967c9C1bf703f", - "balance":1000000000000 - } - ], - "tx_cost": { - "Tx_SetProcessStatus": 1, - "Tx_SetProcessCensus": 1, - "Tx_SetProcessResults": 1, - "Tx_SetProcessQuestionIndex": 1, - "Tx_RegisterKey": 1, - "Tx_NewProcess": 10, - "Tx_SendTokens": 2, - "Tx_CreateAccount": 2, - "Tx_SetAccountInfoURI": 2, - "Tx_AddDelegateForAccount": 2, - "Tx_DelDelegateForAccount": 2, - "Tx_CollectFaucet": 1, - "Tx_SetAccountValidator": 100 - } - } -} diff --git a/service/vochain.go b/service/vochain.go index 33c2dd5cf..cdb239d14 100644 --- a/service/vochain.go +++ b/service/vochain.go @@ -5,14 +5,15 @@ import ( "fmt" "net" "os" + "path/filepath" "strings" "time" - "go.vocdoni.io/dvote/crypto/ethereum" + "go.vocdoni.io/dvote/config" "go.vocdoni.io/dvote/log" "go.vocdoni.io/dvote/util" "go.vocdoni.io/dvote/vochain" - vocdoniGenesis "go.vocdoni.io/dvote/vochain/genesis" + "go.vocdoni.io/dvote/vochain/genesis" "go.vocdoni.io/dvote/vochain/vochaininfo" ) @@ -48,57 +49,51 @@ func (vs *VocdoniService) Vochain() error { "address", vs.Config.PublicAddr, "listenHost", vs.Config.P2PListen) - // Genesis file - var genesisBytes []byte - var err error - // If genesis file from flag, prioritize it + // If genesis flag defined, use file as-is, never overwrite it, and fail if it doesn't exist if len(vs.Config.Genesis) > 0 { log.Infof("using custom genesis from %s", vs.Config.Genesis) if _, err := os.Stat(vs.Config.Genesis); os.IsNotExist(err) { return err } - if genesisBytes, err = os.ReadFile(vs.Config.Genesis); err != nil { - return err - } - } else { // If genesis flag not defined, use a hardcoded or local genesis - genesisBytes, err = os.ReadFile(vs.Config.DataDir + "/config/genesis.json") + } else { // If genesis flag not defined, use local or hardcoded genesis + vs.Config.Genesis = filepath.Join(vs.Config.DataDir, config.DefaultGenesisPath) - if err == nil { // If genesis file found - log.Info("found genesis file") + if _, err := os.Stat(vs.Config.Genesis); err != nil { + if os.IsNotExist(err) { + log.Info("genesis file does not exist, will use hardcoded genesis") + } else { + return fmt.Errorf("unexpected error reading genesis: %w", err) + } + } else { + log.Info("found local genesis file") + loadedGenesis, err := genesis.LoadFromFile(vs.Config.Genesis) + if err != nil { + return fmt.Errorf("couldn't parse local genesis file: %w", err) + } // compare genesis - if !bytes.Equal(ethereum.HashRaw(genesisBytes), vocdoniGenesis.Genesis[vs.Config.Network].Genesis.Hash()) { - // if auto-update genesis enabled, delete local genesis and use hardcoded genesis - if vocdoniGenesis.Genesis[vs.Config.Network].AutoUpdateGenesis || vs.Config.Dev { - log.Warn("new genesis found, cleaning and restarting Vochain") + if !bytes.Equal(loadedGenesis.Hash(), genesis.HardcodedForNetwork(vs.Config.Network).Hash()) { + switch { + case vs.Config.AutoWipeDataDir: + log.Warn("local and hardcoded genesis are different, wiping out vochain datadir") if err = os.RemoveAll(vs.Config.DataDir); err != nil { return err } - if _, ok := vocdoniGenesis.Genesis[vs.Config.Network]; !ok { - err = fmt.Errorf("cannot find a valid genesis for the %q network", vs.Config.Network) + case vs.Config.AutoWipeCometBFT: + log.Warn("local and hardcoded genesis are different, wiping out cometbft datadir") + if err = os.RemoveAll(filepath.Join(vs.Config.DataDir, config.DefaultCometBFTPath)); err != nil { return err } - genesisBytes = vocdoniGenesis.Genesis[vs.Config.Network].Genesis.Marshal() - } else { + default: log.Warn("local and hardcoded genesis are different, risk of potential consensus failure") } } else { - log.Info("local and factory genesis match") - } - } else { // If genesis file not found - if !os.IsNotExist(err) { - return err - } - log.Info("genesis file does not exist, using factory") - if _, ok := vocdoniGenesis.Genesis[vs.Config.Network]; !ok { - err = fmt.Errorf("cannot find a valid genesis for the %s network", vs.Config.Network) - return err + log.Info("local and hardcoded genesis match") } - genesisBytes = vocdoniGenesis.Genesis[vs.Config.Network].Genesis.Marshal() } } // Create the vochain node - vs.App = vochain.NewVochain(vs.Config, genesisBytes) + vs.App = vochain.NewVochain(vs.Config) return nil } diff --git a/vochain/app.go b/vochain/app.go index 857a57471..da010725c 100644 --- a/vochain/app.go +++ b/vochain/app.go @@ -20,6 +20,7 @@ import ( "go.vocdoni.io/dvote/snapshot" "go.vocdoni.io/dvote/test/testcommon/testutil" "go.vocdoni.io/dvote/types" + "go.vocdoni.io/dvote/vochain/genesis" "go.vocdoni.io/dvote/vochain/ist" vstate "go.vocdoni.io/dvote/vochain/state" "go.vocdoni.io/dvote/vochain/transaction" @@ -93,7 +94,7 @@ type BaseApplication struct { chainID string dataDir string dbType string - genesisInfo *comettypes.GenesisDoc + genesisDoc *genesis.Doc // lastDeliverTxResponse is used to store the last DeliverTxResponse, so validators // can skip block re-execution on FinalizeBlock call. @@ -175,7 +176,7 @@ func NewBaseApplication(vochainCfg *config.VochainCfg) (*BaseApplication, error) dataDir: vochainCfg.DataDir, dbType: vochainCfg.DBType, snapshotInterval: vochainCfg.SnapshotInterval, - genesisInfo: &comettypes.GenesisDoc{}, + genesisDoc: &genesis.Doc{}, }, nil } @@ -393,9 +394,9 @@ func (app *BaseApplication) MempoolDeleteTx(txID [32]byte) { } } -// Genesis returns the tendermint genesis information -func (app *BaseApplication) Genesis() *comettypes.GenesisDoc { - return app.genesisInfo +// Genesis returns the genesis used by the app (and cometbft) +func (app *BaseApplication) Genesis() *genesis.Doc { + return app.genesisDoc } // SetZkCircuit ensures the global ZkCircuit is the correct for a chain that implements forks diff --git a/vochain/appsetup.go b/vochain/appsetup.go index d29e206bf..0430e8b4a 100644 --- a/vochain/appsetup.go +++ b/vochain/appsetup.go @@ -20,9 +20,9 @@ const ( ) // SetNode initializes the cometbft consensus node service and client. -func (app *BaseApplication) SetNode(vochaincfg *config.VochainCfg, genesis []byte) error { +func (app *BaseApplication) SetNode(vochaincfg *config.VochainCfg) error { var err error - if app.Node, err = newTendermint(app, vochaincfg, genesis); err != nil { + if app.Node, err = newTendermint(app, vochaincfg); err != nil { return fmt.Errorf("could not set tendermint node service: %s", err) } if vochaincfg.IsSeedNode { @@ -30,11 +30,6 @@ func (app *BaseApplication) SetNode(vochaincfg *config.VochainCfg, genesis []byt } // Note that cometcli.New logs any error rather than returning it. app.NodeClient = cometcli.New(app.Node) - nodeGenesis, err := app.NodeClient.Genesis(context.TODO()) - if err != nil { - return err - } - app.genesisInfo = nodeGenesis.Genesis return nil } diff --git a/vochain/apputils.go b/vochain/apputils.go index ba7cdfeee..2e6f13e12 100644 --- a/vochain/apputils.go +++ b/vochain/apputils.go @@ -21,7 +21,6 @@ import ( cometp2p "github.com/cometbft/cometbft/p2p" cometprivval "github.com/cometbft/cometbft/privval" cometrpchttp "github.com/cometbft/cometbft/rpc/client/http" - comettypes "github.com/cometbft/cometbft/types" ethcommon "github.com/ethereum/go-ethereum/common" ) @@ -91,49 +90,11 @@ func GenerateFaucetPackage(from *ethereum.SignKeys, to ethcommon.Address, amount // NewTemplateGenesisFile creates a genesis file with the given number of validators and its private keys. // Also includes faucet account. // The genesis document is returned. -func NewTemplateGenesisFile(dir string, validators int) (*comettypes.GenesisDoc, error) { - gd := comettypes.GenesisDoc{} +func NewTemplateGenesisFile(dir string, validators int) (*genesis.Doc, error) { + gd := genesis.HardcodedForNetwork("test") gd.ChainID = "test-chain-1" gd.GenesisTime = time.Now() gd.InitialHeight = 0 - gd.ConsensusParams = comettypes.DefaultConsensusParams() - gd.ConsensusParams.Block.MaxBytes = 5242880 - gd.ConsensusParams.Block.MaxGas = -1 - gd.ConsensusParams.Evidence.MaxAgeNumBlocks = 100000 - gd.ConsensusParams.Evidence.MaxAgeDuration = 10000 - gd.ConsensusParams.Validator.PubKeyTypes = []string{"secp256k1"} - - // Create validators - appStateValidators := []genesis.AppStateValidators{} - for i := 0; i < validators; i++ { - nodeDir := filepath.Join(dir, fmt.Sprintf("node%d", i)) - if err := os.MkdirAll(nodeDir, 0o700); err != nil { - return nil, err - } - pk := crypto256k1.GenPrivKey() - privKeyHex := hex.EncodeToString(pk.Bytes()) - pv, err := NewPrivateValidator(privKeyHex, - filepath.Join(nodeDir, "priv_validator_key.json"), - filepath.Join(nodeDir, "priv_validator_state.json"), - ) - if err != nil { - return nil, fmt.Errorf("cannot create validator key and state: (%v)", err) - } - pv.Save() - if err := os.WriteFile(filepath.Join(nodeDir, "hex_priv_key"), []byte(privKeyHex), 0o600); err != nil { - return nil, err - } - signer := ethereum.SignKeys{} - if err := signer.AddHexKey(hex.EncodeToString(pv.Key.PrivKey.Bytes())); err != nil { - return nil, err - } - appStateValidators = append(appStateValidators, genesis.AppStateValidators{ - Address: signer.Address().Bytes(), - PubKey: pv.Key.PubKey.Bytes(), - Power: 10, - KeyIndex: uint8(i + 1), // zero is reserved for disabling validator key keeper capabilities - }) - } // Faucet faucet := ethereum.SignKeys{} @@ -168,7 +129,6 @@ func NewTemplateGenesisFile(dir string, validators int) (*comettypes.GenesisDoc, // Build genesis app state and create genesis file appState := genesis.AppState{ - Validators: appStateValidators, Accounts: []genesis.Account{ { Address: faucet.Address().Bytes(), @@ -178,12 +138,45 @@ func NewTemplateGenesisFile(dir string, validators int) (*comettypes.GenesisDoc, TxCost: genesis.TransactionCosts{}, } appState.MaxElectionSize = 100000 + + // Create validators + for i := 0; i < validators; i++ { + nodeDir := filepath.Join(dir, fmt.Sprintf("node%d", i)) + if err := os.MkdirAll(nodeDir, 0o700); err != nil { + return nil, err + } + pk := crypto256k1.GenPrivKey() + privKeyHex := hex.EncodeToString(pk.Bytes()) + pv, err := NewPrivateValidator(privKeyHex, + filepath.Join(nodeDir, "priv_validator_key.json"), + filepath.Join(nodeDir, "priv_validator_state.json"), + ) + if err != nil { + return nil, fmt.Errorf("cannot create validator key and state: (%v)", err) + } + pv.Save() + if err := os.WriteFile(filepath.Join(nodeDir, "hex_priv_key"), []byte(privKeyHex), 0o600); err != nil { + return nil, err + } + signer := ethereum.SignKeys{} + if err := signer.AddHexKey(hex.EncodeToString(pv.Key.PrivKey.Bytes())); err != nil { + return nil, err + } + appState.Validators = append(appState.Validators, genesis.AppStateValidators{ + Address: signer.Address().Bytes(), + PubKey: pv.Key.PubKey.Bytes(), + Power: 10, + Name: fmt.Sprintf("validator%d", i), + KeyIndex: uint8(i + 1), // zero is reserved for disabling validator key keeper capabilities + }) + } + appStateBytes, err := json.Marshal(appState) if err != nil { return nil, err } gd.AppState = appStateBytes - return &gd, gd.SaveAs(filepath.Join(dir, "genesis.json")) + return gd, gd.SaveAs(filepath.Join(dir, "genesis.json")) } // newCometRPCClient sets up a new cometbft RPC client diff --git a/vochain/cometbft.go b/vochain/cometbft.go index 2565b2e6c..5e3fa0550 100644 --- a/vochain/cometbft.go +++ b/vochain/cometbft.go @@ -36,22 +36,29 @@ import ( // The returned AppVersion will be included in the Header of every block. // Tendermint expects LastBlockAppHash and LastBlockHeight to be updated during Commit, // ensuring that Commit is never called twice for the same block height. -// -// We use this method to initialize some state variables. func (app *BaseApplication) Info(_ context.Context, req *cometabcitypes.InfoRequest) (*cometabcitypes.InfoResponse, error) { + // TODO: move SetElectionPriceCalc() somewhere else, also deduplicating from InitChain + if err := app.State.SetElectionPriceCalc(); err != nil { + return nil, fmt.Errorf("cannot set election price calc: %w", err) + } + + log.Infow("cometbft info", "cometVersion", req.Version, "p2pVersion", + req.P2PVersion, "blockVersion", req.BlockVersion) + lastHeight, err := app.State.LastHeight() if err != nil { return nil, fmt.Errorf("cannot get State.LastHeight: %w", err) } - app.State.SetHeight(lastHeight) - appHash := app.State.CommittedHash() - if err := app.State.SetElectionPriceCalc(); err != nil { - return nil, fmt.Errorf("cannot set election price calc: %w", err) + if lastHeight < uint32(app.Genesis().InitialHeight) { + // when the genesis has InitialHeight > 0, means we're doing a chain bump + // and we need cometbft to trigger InitChain, which only happens when the app reports lastHeight=0 + log.Warnf("genesis initial height (%d) > our state lastHeight (%d), creating new chain on top of current one", + app.Genesis().InitialHeight, lastHeight) + lastHeight = 0 } + appHash := app.State.CommittedHash() // print some basic version info about tendermint components - log.Infow("cometbft info", "cometVersion", req.Version, "p2pVersion", - req.P2PVersion, "blockVersion", req.BlockVersion) log.Infow("telling cometbft our state", "LastBlockHeight", lastHeight, "LastBlockAppHash", hex.EncodeToString(appHash), diff --git a/vochain/genesis/genesis.go b/vochain/genesis/genesis.go index a0e3f0aec..e1368550c 100644 --- a/vochain/genesis/genesis.go +++ b/vochain/genesis/genesis.go @@ -1,107 +1,138 @@ package genesis import ( + "encoding/json" "time" + cometsecp256k1 "github.com/cometbft/cometbft/crypto/secp256k1" + comettypes "github.com/cometbft/cometbft/types" + "go.vocdoni.io/dvote/types" ) -// Genesis is a map containing the default Genesis details -var Genesis = map[string]Vochain{ +// genesis is a map containing the default GenesisDoc for each network +var genesis = map[string]comettypes.GenesisDoc{ + // testsuite test network + "test": testGenesis, + // Development network - "dev": { - AutoUpdateGenesis: true, - SeedNodes: []string{ - "7440a5b086e16620ce7b13198479016aa2b07988@seed.dev.vocdoni.net:26656", - }, - StateSync: map[string]StateSyncParams{ - "vocdoni/DEV/32": { - TrustHeight: 10000, - TrustHash: types.HexStringToHexBytes("0x2b430478c7867dc078c0380b81838d75358db7c8b65bfaf84ade85448a0abd54"), - }, - }, - Genesis: &devGenesis, - }, + "dev": devGenesis, // Staging network - "stage": { - AutoUpdateGenesis: true, - SeedNodes: []string{ - "588133b8309363a2a852e853424251cd6e8c5330@seed.stg.vocdoni.net:26656", - }, - StateSync: map[string]StateSyncParams{ - "Vocdoni/STAGE/11": { - TrustHeight: 150000, - TrustHash: types.HexStringToHexBytes("0xd964cd5ec4704d3b3e1864c174edd1331044926bb2e6d3fe0b239b1c59329ff2"), - }, - }, - Genesis: &stageGenesis, - }, + "stage": stageGenesis, // LTS production network - "lts": { - AutoUpdateGenesis: false, - SeedNodes: []string{ - "32acbdcda649fbcd35775f1dd8653206d940eee4@seed1.lts.vocdoni.net:26656", - "02bfac9bd98bf25429d12edc50552cca5e975080@seed2.lts.vocdoni.net:26656", + "lts": ltsGenesis, +} + +var testGenesis = comettypes.GenesisDoc{ + GenesisTime: time.Date(2024, time.May, 7, 1, 0, 0, 0, time.UTC), + ChainID: "vocdoni/TEST/1", + ConsensusParams: &comettypes.ConsensusParams{ + Block: DefaultBlockParams(), + Evidence: comettypes.DefaultEvidenceParams(), + Validator: DefaultValidatorParams(), + Version: comettypes.VersionParams{ + App: 1, }, - StateSync: map[string]StateSyncParams{ - "Vocdoni/LTS/1.2": { - TrustHeight: 1000000, - TrustHash: types.HexStringToHexBytes("0xd782c4a8e889a12fb326dd7f098336756f4238169a603501ae4a2b2f88c19db9"), + }, + AppState: jsonRawMessage(AppState{ + MaxElectionSize: 1000000, + NetworkCapacity: 10000, + Validators: []AppStateValidators{ + { // 0 + Address: hexStringToPubKey("038faa051e8a726597549bb057f1d296947bb54378443ec8fce030001ece678e14").Address().Bytes(), + PubKey: hexStringToPubKey("038faa051e8a726597549bb057f1d296947bb54378443ec8fce030001ece678e14").Bytes(), + Power: 10, + Name: "validator1", + KeyIndex: 1, + }, + { // 1 + Address: hexStringToPubKey("03cf8d0d1afa561e01145a275d1e41ed1a6d652361509a4c93dfc6488fdf5eca38").Address().Bytes(), + PubKey: hexStringToPubKey("03cf8d0d1afa561e01145a275d1e41ed1a6d652361509a4c93dfc6488fdf5eca38").Bytes(), + Power: 10, + Name: "validator2", + KeyIndex: 2, + }, + { // 2 + Address: hexStringToPubKey("031802916d945239a39a9a8ee3e2eb3fb91ee324ccdfd73659f482e644892b796f").Address().Bytes(), + PubKey: hexStringToPubKey("031802916d945239a39a9a8ee3e2eb3fb91ee324ccdfd73659f482e644892b796f").Bytes(), + Power: 10, + Name: "validator3", + KeyIndex: 3, + }, + { // 3 + Address: hexStringToPubKey("02a790726e98978b0ca2cde3a09cbb1af1b298191f46e051b86bcb1854deb58478").Address().Bytes(), + PubKey: hexStringToPubKey("02a790726e98978b0ca2cde3a09cbb1af1b298191f46e051b86bcb1854deb58478").Bytes(), + Power: 10, + Name: "validator4", + KeyIndex: 4, }, }, - Genesis: <sGenesis, - }, + Accounts: []Account{ + { // faucet + Address: types.HexStringToHexBytes("0x88a499cEf9D1330111b41360173967c9C1bf703f"), + Balance: 1000000000000, + }, + }, + TxCost: TransactionCosts{ + SetProcessStatus: 1, + SetProcessCensus: 1, + SetProcessQuestionIndex: 1, + RegisterKey: 1, + NewProcess: 10, + SendTokens: 2, + SetAccountInfoURI: 2, + CreateAccount: 2, + AddDelegateForAccount: 2, + DelDelegateForAccount: 2, + CollectFaucet: 1, + SetAccountSIK: 1, + DelAccountSIK: 1, + SetAccountValidator: 100, + }, + }), } -var devGenesis = Doc{ +var devGenesis = comettypes.GenesisDoc{ GenesisTime: time.Date(2024, time.April, 4, 1, 0, 0, 0, time.UTC), ChainID: "vocdoni/DEV/33", - ConsensusParams: &ConsensusParams{ - Block: BlockParams{ - MaxBytes: 2097152, - MaxGas: -1, - }, - Evidence: EvidenceParams{ - MaxAgeNumBlocks: 100000, - MaxAgeDuration: 10000, - }, - Validator: ValidatorParams{ - PubKeyTypes: []string{"secp256k1"}, - }, - Version: VersionParams{ - AppVersion: 1, + ConsensusParams: &comettypes.ConsensusParams{ + Block: DefaultBlockParams(), + Evidence: comettypes.DefaultEvidenceParams(), + Validator: DefaultValidatorParams(), + Version: comettypes.VersionParams{ + App: 1, }, }, - AppState: AppState{ + AppState: jsonRawMessage(AppState{ MaxElectionSize: 100000, NetworkCapacity: 20000, Validators: []AppStateValidators{ { // 0 - Address: types.HexStringToHexBytes("04cc36be85a0a6e2bfd09295396625e6302d7c60"), - PubKey: types.HexStringToHexBytes("03c61c8399828b0c5644455e43c946979272dc3ca0859267f798268802303015f7"), + Address: hexStringToPubKey("03c61c8399828b0c5644455e43c946979272dc3ca0859267f798268802303015f7").Address().Bytes(), + PubKey: hexStringToPubKey("03c61c8399828b0c5644455e43c946979272dc3ca0859267f798268802303015f7").Bytes(), Power: 10, Name: "", KeyIndex: 1, }, { // 1 - Address: types.HexStringToHexBytes("fc095a35338d96503b6fd1010475e45a3545fc25"), - PubKey: types.HexStringToHexBytes("0383fe95c5fddee9932ef0f77c180c3c5d0357dba566f2ee77de666a64d9d8c2a6"), + Address: hexStringToPubKey("0383fe95c5fddee9932ef0f77c180c3c5d0357dba566f2ee77de666a64d9d8c2a6").Address().Bytes(), + PubKey: hexStringToPubKey("0383fe95c5fddee9932ef0f77c180c3c5d0357dba566f2ee77de666a64d9d8c2a6").Bytes(), Power: 10, Name: "", KeyIndex: 2, }, { // 2 - Address: types.HexStringToHexBytes("a9b1008f17654b36f2a9abd29323c53d344415a0"), - PubKey: types.HexStringToHexBytes("03503c0872bdcd804b1635cf187577ca1caddbbb14ec8eb3af68579fe4bedcf071"), + Address: hexStringToPubKey("03503c0872bdcd804b1635cf187577ca1caddbbb14ec8eb3af68579fe4bedcf071").Address().Bytes(), + PubKey: hexStringToPubKey("03503c0872bdcd804b1635cf187577ca1caddbbb14ec8eb3af68579fe4bedcf071").Bytes(), Power: 10, Name: "", KeyIndex: 3, }, { // 3 - Address: types.HexStringToHexBytes("234120598e3fcfcfae5d969254d371248b0cf8d1"), - PubKey: types.HexStringToHexBytes("02159b8dd9b1cea02cd0ff78ae26dc8aa4efc65f46511537d8550fe1ce407100c3"), + Address: hexStringToPubKey("02159b8dd9b1cea02cd0ff78ae26dc8aa4efc65f46511537d8550fe1ce407100c3").Address().Bytes(), + PubKey: hexStringToPubKey("02159b8dd9b1cea02cd0ff78ae26dc8aa4efc65f46511537d8550fe1ce407100c3").Bytes(), Power: 10, Name: "", KeyIndex: 4, @@ -137,80 +168,68 @@ var devGenesis = Doc{ DelAccountSIK: 1, SetAccountValidator: 10000, }, - }, + }), } -var stageGenesis = Doc{ +var stageGenesis = comettypes.GenesisDoc{ GenesisTime: time.Date(2024, time.January, 30, 1, 0, 0, 0, time.UTC), ChainID: "vocdoni/STAGE/11", - ConsensusParams: &ConsensusParams{ - Block: BlockParams{ - MaxBytes: 2097152, - MaxGas: -1, - }, - Evidence: EvidenceParams{ - MaxAgeNumBlocks: 100000, - MaxAgeDuration: 10000, - }, - Validator: ValidatorParams{ - PubKeyTypes: []string{"secp256k1"}, - }, - Version: VersionParams{ - AppVersion: 1, + ConsensusParams: &comettypes.ConsensusParams{ + Block: DefaultBlockParams(), + Evidence: comettypes.DefaultEvidenceParams(), + Validator: DefaultValidatorParams(), + Version: comettypes.VersionParams{ + App: 1, }, }, - AppState: AppState{ + AppState: jsonRawMessage(AppState{ MaxElectionSize: 500000, NetworkCapacity: 10000, Validators: []AppStateValidators{ { // 0 - Address: types.HexStringToHexBytes("321d141cf1fcb41d7844af611b5347afc380a03f"), - PubKey: types.HexStringToHexBytes("02420b2ee645b9509453cd3b99a6bd8e5e10c1d746fb0bb0ac5af79aba19bb9784"), + Address: hexStringToPubKey("02420b2ee645b9509453cd3b99a6bd8e5e10c1d746fb0bb0ac5af79aba19bb9784").Address().Bytes(), + PubKey: hexStringToPubKey("02420b2ee645b9509453cd3b99a6bd8e5e10c1d746fb0bb0ac5af79aba19bb9784").Bytes(), Power: 10, Name: "vocdoni1", KeyIndex: 1, }, { // 1 - Address: types.HexStringToHexBytes("5e6c49d98ff3b90ca46387d7c583d20cf99f29bd"), - PubKey: types.HexStringToHexBytes("03e6c55195825f9736ce8a4553913bbadb26c7f094540e06aed9ccda0e6e26050d"), - Power: 10, - Name: "vocdoni2", - KeyIndex: 0, + Address: hexStringToPubKey("03e6c55195825f9736ce8a4553913bbadb26c7f094540e06aed9ccda0e6e26050d").Address().Bytes(), + PubKey: hexStringToPubKey("03e6c55195825f9736ce8a4553913bbadb26c7f094540e06aed9ccda0e6e26050d").Bytes(), + Power: 10, + Name: "vocdoni2", }, { // 2 - Address: types.HexStringToHexBytes("9d4c46f7485036faea5f15c3034e9e864b9415b5"), - PubKey: types.HexStringToHexBytes("03cb39e1132eee0b25ec75d7dad1f2885460f9b2f200d108a923b78e648b783839"), - Power: 10, - Name: "vocdoni3", - KeyIndex: 0, + Address: hexStringToPubKey("03cb39e1132eee0b25ec75d7dad1f2885460f9b2f200d108a923b78e648b783839").Address().Bytes(), + PubKey: hexStringToPubKey("03cb39e1132eee0b25ec75d7dad1f2885460f9b2f200d108a923b78e648b783839").Bytes(), + Power: 10, + Name: "vocdoni3", }, { // 3 - Address: types.HexStringToHexBytes("52d74938f81569aba46f384c8108c370b5403585"), - PubKey: types.HexStringToHexBytes("03f6c246831a524e8214e9ceb61d3da2c3c4dbee09bcbe5d9d9878aaa085764d65"), + Address: hexStringToPubKey("03f6c246831a524e8214e9ceb61d3da2c3c4dbee09bcbe5d9d9878aaa085764d65").Address().Bytes(), + PubKey: hexStringToPubKey("03f6c246831a524e8214e9ceb61d3da2c3c4dbee09bcbe5d9d9878aaa085764d65").Bytes(), Power: 10, Name: "vocdoni4", KeyIndex: 2, }, { // 4 - Address: types.HexStringToHexBytes("ad6ff21ccfb31002adc52714043e37da1b555b15"), - PubKey: types.HexStringToHexBytes("02fd283ff5760958b4e59eac6b0647ed002669ef2862eb9361251376160aa72fe5"), - Power: 10, - Name: "vocdoni5", - KeyIndex: 0, + Address: hexStringToPubKey("02fd283ff5760958b4e59eac6b0647ed002669ef2862eb9361251376160aa72fe5").Address().Bytes(), + PubKey: hexStringToPubKey("02fd283ff5760958b4e59eac6b0647ed002669ef2862eb9361251376160aa72fe5").Bytes(), + Power: 10, + Name: "vocdoni5", }, { // 5 - Address: types.HexStringToHexBytes("8367a1488c3afda043a2a602c13f01d801d0270e"), - PubKey: types.HexStringToHexBytes("03369a8c595c70526baf8528b908591ec286e910b10796c3d6dfca0ef76a645167"), + Address: hexStringToPubKey("03369a8c595c70526baf8528b908591ec286e910b10796c3d6dfca0ef76a645167").Address().Bytes(), + PubKey: hexStringToPubKey("03369a8c595c70526baf8528b908591ec286e910b10796c3d6dfca0ef76a645167").Bytes(), Power: 10, Name: "vocdoni6", KeyIndex: 3, }, { // 6 - Address: types.HexStringToHexBytes("4146598ff76009f45903958c4c7a3195683b2f61"), - PubKey: types.HexStringToHexBytes("02b5005aeefdb8bb196d308df3fba157a7c1e84966f899a9def6aa97b086bc87e7"), - Power: 10, - Name: "vocdoni7", - KeyIndex: 0, + Address: hexStringToPubKey("02b5005aeefdb8bb196d308df3fba157a7c1e84966f899a9def6aa97b086bc87e7").Address().Bytes(), + PubKey: hexStringToPubKey("02b5005aeefdb8bb196d308df3fba157a7c1e84966f899a9def6aa97b086bc87e7").Bytes(), + Power: 10, + Name: "vocdoni7", }, }, Accounts: []Account{ @@ -235,91 +254,78 @@ var stageGenesis = Doc{ DelAccountSIK: 1, SetAccountValidator: 500000, }, - }, + }), } -var ltsGenesis = Doc{ - GenesisTime: time.Date(2023, time.October, 24, 9, 0, 0, 0, time.UTC), +var ltsGenesis = comettypes.GenesisDoc{ + GenesisTime: time.Date(2024, time.April, 24, 9, 0, 0, 0, time.UTC), ChainID: "vocdoni/LTS/1.2", - ConsensusParams: &ConsensusParams{ - Block: BlockParams{ - MaxBytes: 2097152, - MaxGas: -1, - }, - Evidence: EvidenceParams{ - MaxAgeNumBlocks: 100000, - MaxAgeDuration: 10000, - }, - Validator: ValidatorParams{ - PubKeyTypes: []string{"secp256k1"}, - }, - Version: VersionParams{ - AppVersion: 0, + ConsensusParams: &comettypes.ConsensusParams{ + Block: DefaultBlockParams(), + Evidence: comettypes.DefaultEvidenceParams(), + Validator: DefaultValidatorParams(), + Version: comettypes.VersionParams{ + App: 0, }, }, - AppState: AppState{ + AppState: jsonRawMessage(AppState{ MaxElectionSize: 1000000, NetworkCapacity: 5000, Validators: []AppStateValidators{ { // 0 - Address: types.HexStringToHexBytes("8a67aa6e63ea24a029fade79b93f39aa2f935608"), - PubKey: types.HexStringToHexBytes("024e3fbcd7e1516ebbc332519a3602e39753c6dd49c46df307c1e60b976f0b29a5"), + Address: hexStringToPubKey("024e3fbcd7e1516ebbc332519a3602e39753c6dd49c46df307c1e60b976f0b29a5").Address().Bytes(), + PubKey: hexStringToPubKey("024e3fbcd7e1516ebbc332519a3602e39753c6dd49c46df307c1e60b976f0b29a5").Bytes(), Power: 10, Name: "vocdoni-validator0", KeyIndex: 1, }, { // 1 - Address: types.HexStringToHexBytes("dd47c5e9db1be4f9c6fac3474b9d9aec5c00ecdd"), - PubKey: types.HexStringToHexBytes("02364db3aedf05ffbf25e67e81de971f3a9965b9e1a2d066af06b634ba5c959152"), - Power: 10, - Name: "vocdoni-validator1", - KeyIndex: 0, + Address: hexStringToPubKey("02364db3aedf05ffbf25e67e81de971f3a9965b9e1a2d066af06b634ba5c959152").Address().Bytes(), + PubKey: hexStringToPubKey("02364db3aedf05ffbf25e67e81de971f3a9965b9e1a2d066af06b634ba5c959152").Bytes(), + Power: 10, + Name: "vocdoni-validator1", }, { // 2 - Address: types.HexStringToHexBytes("6bc0fe0ac7e7371294e3c2d39b0e1337b9757193"), - PubKey: types.HexStringToHexBytes("037a2e3b3e7ae07cb75dbc73aff9c39b403e0ec58b596cf03fe99a27555285ef73"), - Power: 10, - Name: "vocdoni-validator2", - KeyIndex: 0, + Address: hexStringToPubKey("037a2e3b3e7ae07cb75dbc73aff9c39b403e0ec58b596cf03fe99a27555285ef73").Address().Bytes(), + PubKey: hexStringToPubKey("037a2e3b3e7ae07cb75dbc73aff9c39b403e0ec58b596cf03fe99a27555285ef73").Bytes(), + Power: 10, + Name: "vocdoni-validator2", }, { // 3 - Address: types.HexStringToHexBytes("d863a79bb3c019941de5ebfc10a136bbfbbc2982"), - PubKey: types.HexStringToHexBytes("03553d1b75cdda0a49136417daee453c3a00ed75af64ec6aa20476cf227dfd946c"), + Address: hexStringToPubKey("03553d1b75cdda0a49136417daee453c3a00ed75af64ec6aa20476cf227dfd946c").Address().Bytes(), + PubKey: hexStringToPubKey("03553d1b75cdda0a49136417daee453c3a00ed75af64ec6aa20476cf227dfd946c").Bytes(), Power: 10, Name: "vocdoni-validator3", KeyIndex: 2, }, { // 4 - Address: types.HexStringToHexBytes("d16a9fe63456ea0b3706a2855b98a3a20f10e308"), - PubKey: types.HexStringToHexBytes("036e25b61605a04ef3cf5829e73a2c9db4a4b0958a8a6be0895c3df19b69e7ad45"), - Power: 10, - Name: "vocdoni-validator4", - KeyIndex: 0, + Address: hexStringToPubKey("036e25b61605a04ef3cf5829e73a2c9db4a4b0958a8a6be0895c3df19b69e7ad45").Address().Bytes(), + PubKey: hexStringToPubKey("036e25b61605a04ef3cf5829e73a2c9db4a4b0958a8a6be0895c3df19b69e7ad45").Bytes(), + Power: 10, + Name: "vocdoni-validator4", }, { // 5 - Address: types.HexStringToHexBytes("4ece41dd2b0f0ddd4ddef9fa83ad6d973c98a48c"), - PubKey: types.HexStringToHexBytes("027b034a05be20113cdf39eff609c5265d1575c5510bf3fcc611e6da0bed6d30b4"), + Address: hexStringToPubKey("027b034a05be20113cdf39eff609c5265d1575c5510bf3fcc611e6da0bed6d30b4").Address().Bytes(), + PubKey: hexStringToPubKey("027b034a05be20113cdf39eff609c5265d1575c5510bf3fcc611e6da0bed6d30b4").Bytes(), Power: 10, Name: "vocdoni-validator5", KeyIndex: 3, }, { // 6 - Address: types.HexStringToHexBytes("2b617bad95bb36805512c76a02144c778d3cda20"), - PubKey: types.HexStringToHexBytes("034105acd3392dffcfe08a7a2e1c48fb4f52c7f4cdce4477474afc0ddff023ec2d"), - Power: 10, - Name: "vocdoni-validator6", - KeyIndex: 0, + Address: hexStringToPubKey("034105acd3392dffcfe08a7a2e1c48fb4f52c7f4cdce4477474afc0ddff023ec2d").Address().Bytes(), + PubKey: hexStringToPubKey("034105acd3392dffcfe08a7a2e1c48fb4f52c7f4cdce4477474afc0ddff023ec2d").Bytes(), + Power: 10, + Name: "vocdoni-validator6", }, { // 7 - Address: types.HexStringToHexBytes("4945fd40d29870a931561b26a30a529081ded677"), - PubKey: types.HexStringToHexBytes("038276c348971ef9d8b11abaf0cdce50e6cb89bd0f87df14301ef02d46db09db6d"), - Power: 10, - Name: "vocdoni-validator7", - KeyIndex: 0, + Address: hexStringToPubKey("038276c348971ef9d8b11abaf0cdce50e6cb89bd0f87df14301ef02d46db09db6d").Address().Bytes(), + PubKey: hexStringToPubKey("038276c348971ef9d8b11abaf0cdce50e6cb89bd0f87df14301ef02d46db09db6d").Bytes(), + Power: 10, + Name: "vocdoni-validator7", }, { // 8 - Address: types.HexStringToHexBytes("34868fa6ef1b001b830a5a19a06c69f62f622410"), - PubKey: types.HexStringToHexBytes("02a94d4a25c0281980af65d014ce72d34b0aba6e5dff362da8b34c31e8b93b26a9"), + Address: hexStringToPubKey("02a94d4a25c0281980af65d014ce72d34b0aba6e5dff362da8b34c31e8b93b26a9").Address().Bytes(), + PubKey: hexStringToPubKey("02a94d4a25c0281980af65d014ce72d34b0aba6e5dff362da8b34c31e8b93b26a9").Bytes(), Power: 10, Name: "vocdoni-validator8", KeyIndex: 4, @@ -351,14 +357,70 @@ var ltsGenesis = Doc{ DelAccountSIK: 1, SetAccountValidator: 500000, }, - }, + }), +} + +// DefaultBlockParams returns different defaults than upstream DefaultBlockParams: +// MaxBytes = 2 megabytes, and MaxGas = -1 +func DefaultBlockParams() comettypes.BlockParams { + return comettypes.BlockParams{ + MaxBytes: 2097152, + MaxGas: -1, + } +} + +// DefaultValidatorParams returns different defaults than upstream DefaultValidatorParams: +// allows only secp256k1 pubkeys. +func DefaultValidatorParams() comettypes.ValidatorParams { + return comettypes.ValidatorParams{ + PubKeyTypes: []string{comettypes.ABCIPubKeyTypeSecp256k1}, + } } // AvailableNetworks returns the list of hardcoded networks func AvailableNetworks() []string { networks := []string{} - for k := range Genesis { + for k := range genesis { networks = append(networks, k) } return networks } + +// HardcodedForNetwork returns the hardcoded genesis.Doc of a specific network. Panics if not found +func HardcodedForNetwork(network string) *Doc { + g, ok := genesis[network] + if !ok { + panic("no hardcoded genesis found for current network") + } + if err := g.ValidateAndComplete(); err != nil { + panic("hardcoded genesis is invalid") + } + return &Doc{g} +} + +// LoadFromFile loads and unmarshals a genesis.json, returning a genesis.Doc +func LoadFromFile(path string) (*Doc, error) { + gd, err := comettypes.GenesisDocFromFile(path) + if err != nil { + return nil, err + } + return &Doc{*gd}, nil +} + +// hexStringToPubKey converts a hex string to a secp256k1.PubKey. +// It strips a leading '0x' or '0X' if found, for backwards compatibility. +// Panics if the string is not a valid hex string. +func hexStringToPubKey(hexString string) cometsecp256k1.PubKey { + return cometsecp256k1.PubKey(types.HexStringToHexBytes(hexString)) +} + +// jsonRawMessage marshals the appState into a json.RawMessage. +// Panics on error. +func jsonRawMessage(appState AppState) json.RawMessage { + jrm, err := json.Marshal(appState) + if err != nil { + // must never happen + panic(err) + } + return jrm +} diff --git a/vochain/genesis/types.go b/vochain/genesis/types.go index 479a50da0..19622f8bc 100644 --- a/vochain/genesis/types.go +++ b/vochain/genesis/types.go @@ -2,58 +2,17 @@ package genesis import ( "encoding/json" - "strconv" - "time" + comethash "github.com/cometbft/cometbft/crypto/tmhash" comettypes "github.com/cometbft/cometbft/types" - "go.vocdoni.io/dvote/crypto/ethereum" "go.vocdoni.io/dvote/types" ) -// Vochain is a struct containing the genesis details. -type Vochain struct { - AutoUpdateGenesis bool - SeedNodes []string - StateSync map[string]StateSyncParams - Genesis *Doc -} - -// The genesis app state types are copied from -// github.com/cometbft/cometbft/types, for the sake of making this package -// lightweight and not have it import heavy indirect dependencies like grpc or -// crypto/*. - -// Doc defines the initial conditions for a Vocdoni blockchain. -// It is mostly a wrapper around the Tendermint GenesisDoc. +// Doc is a wrapper around the CometBFT GenesisDoc, +// that adds some useful methods like Hash type Doc struct { - GenesisTime time.Time `json:"genesis_time"` - ChainID string `json:"chain_id"` - ConsensusParams *ConsensusParams `json:"consensus_params,omitempty"` - AppHash types.HexBytes `json:"app_hash"` - AppState AppState `json:"app_state,omitempty"` -} - -// TendermintDoc returns the Tendermint GenesisDoc from the Vocdoni genesis.Doc. -func (g *Doc) TendermintDoc() comettypes.GenesisDoc { - appState, err := json.Marshal(g.AppState) - if err != nil { - // must never happen - panic(err) - } - return comettypes.GenesisDoc{ - GenesisTime: g.GenesisTime, - ChainID: g.ChainID, - ConsensusParams: &comettypes.ConsensusParams{ - Block: comettypes.BlockParams{ - MaxBytes: int64(g.ConsensusParams.Block.MaxBytes), - MaxGas: int64(g.ConsensusParams.Block.MaxGas), - }, - }, - Validators: []comettypes.GenesisValidator{}, - AppHash: []byte(g.AppHash), - AppState: appState, - } + comettypes.GenesisDoc } // Marshal returns the JSON encoding of the genesis.Doc. @@ -71,50 +30,11 @@ func (g *Doc) Hash() []byte { if err != nil { panic(err) } - return ethereum.HashRaw(data) -} - -// ConsensusParams defines the consensus critical parameters that determine the -// validity of blocks. This comes from Tendermint. -type ConsensusParams struct { - Block BlockParams `json:"block"` - Evidence EvidenceParams `json:"evidence"` - Validator ValidatorParams `json:"validator"` - Version VersionParams `json:"version"` -} - -// BlockParams define limits on the block size and gas plus minimum time -// between blocks. This comes from Tendermint. -type BlockParams struct { - MaxBytes StringifiedInt64 `json:"max_bytes"` - MaxGas StringifiedInt64 `json:"max_gas"` -} - -// EvidenceParams define limits on max evidence age and max duration -type EvidenceParams struct { - MaxAgeNumBlocks StringifiedInt64 `json:"max_age_num_blocks"` - // only accept new evidence more recent than this - MaxAgeDuration StringifiedInt64 `json:"max_age_duration"` -} - -// ValidatorParams define the validator key -type ValidatorParams struct { - PubKeyTypes []string `json:"pub_key_types"` -} - -// VersionParams define the version app information -type VersionParams struct { - AppVersion StringifiedInt64 `json:"app_version"` + return comethash.Sum(data) } // ________________________ GENESIS APP STATE ________________________ -// Account represents an account in the genesis app state -type Account struct { - Address types.HexBytes `json:"address"` - Balance uint64 `json:"balance"` -} - // AppState is the main application state in the genesis file. type AppState struct { Validators []AppStateValidators `json:"validators"` @@ -133,33 +53,8 @@ type AppStateValidators struct { KeyIndex uint8 `json:"key_index"` } -// StateSyncParams define the parameters used by StateSync -type StateSyncParams struct { - TrustHeight int64 - TrustHash types.HexBytes -} - -// StringifiedInt64 is a wrapper around int64 that marshals/unmarshals as a string. -// This is a dirty non-sense workaround. Blame Tendermint not me. -// For some (unknown) reason Tendermint requires the integer values to be strings in -// the JSON genesis file. -type StringifiedInt64 int64 - -// MarshalJSON implements the json.Marshaler interface. -func (i StringifiedInt64) MarshalJSON() ([]byte, error) { - return json.Marshal(strconv.FormatInt(int64(i), 10)) -} - -// UnmarshalJSON implements the json.Unmarshaler interface. -func (i *StringifiedInt64) UnmarshalJSON(data []byte) error { - var s string - if err := json.Unmarshal(data, &s); err != nil { - return err - } - v, err := strconv.ParseInt(s, 10, 64) - if err != nil { - return err - } - *i = StringifiedInt64(v) - return nil +// Account represents an account in the genesis app state +type Account struct { + Address types.HexBytes `json:"address"` + Balance uint64 `json:"balance"` } diff --git a/vochain/start.go b/vochain/start.go index c8c1616ad..cbc9b0b7f 100644 --- a/vochain/start.go +++ b/vochain/start.go @@ -4,7 +4,6 @@ package vochain import ( "context" "encoding/hex" - "encoding/json" "fmt" "os" "path/filepath" @@ -14,7 +13,7 @@ import ( "go.vocdoni.io/dvote/config" "go.vocdoni.io/dvote/crypto/ethereum" "go.vocdoni.io/dvote/crypto/zk/circuit" - vocdoniGenesis "go.vocdoni.io/dvote/vochain/genesis" + "go.vocdoni.io/dvote/vochain/genesis" cometconfig "github.com/cometbft/cometbft/config" cometp2p "github.com/cometbft/cometbft/p2p" @@ -25,7 +24,7 @@ import ( ) // NewVochain starts a node with an ABCI application -func NewVochain(vochaincfg *config.VochainCfg, genesis []byte) *BaseApplication { +func NewVochain(vochaincfg *config.VochainCfg) *BaseApplication { // creating new vochain app c := *vochaincfg c.DataDir = filepath.Join(vochaincfg.DataDir, "data") @@ -34,7 +33,7 @@ func NewVochain(vochaincfg *config.VochainCfg, genesis []byte) *BaseApplication log.Fatalf("cannot initialize vochain application: %s", err) } log.Info("creating tendermint node and application") - err = app.SetNode(vochaincfg, genesis) + err = app.SetNode(vochaincfg) if err != nil { log.Fatal(err) } @@ -46,16 +45,16 @@ func NewVochain(vochaincfg *config.VochainCfg, genesis []byte) *BaseApplication } // newTendermint creates a new tendermint node attached to the given ABCI app -func newTendermint(app *BaseApplication, - localConfig *config.VochainCfg, genesis []byte) (*cometnode.Node, error) { +func newTendermint(app *BaseApplication, localConfig *config.VochainCfg) (*cometnode.Node, error) { var err error tconfig := cometconfig.DefaultConfig() - tconfig.SetRoot(localConfig.DataDir) - if err := os.MkdirAll(filepath.Join(localConfig.DataDir, "config"), 0750); err != nil { + + tconfig.SetRoot(filepath.Join(localConfig.DataDir, config.DefaultCometBFTPath)) + if err := os.MkdirAll(filepath.Join(tconfig.RootDir, cometconfig.DefaultConfigDir), 0o750); err != nil { log.Fatal(err) } - if err := os.MkdirAll(filepath.Join(localConfig.DataDir, "data"), 0750); err != nil { + if err := os.MkdirAll(filepath.Join(tconfig.RootDir, cometconfig.DefaultDataDir), 0o750); err != nil { log.Fatal(err) } @@ -69,9 +68,10 @@ func newTendermint(app *BaseApplication, tconfig.P2P.AddrBookStrict = false } tconfig.P2P.Seeds = strings.Trim(strings.Join(localConfig.Seeds, ","), "[]\"") - if _, ok := vocdoniGenesis.Genesis[localConfig.Network]; len(tconfig.P2P.Seeds) < 8 && - !localConfig.IsSeedNode && ok { - tconfig.P2P.Seeds = strings.Join(vocdoniGenesis.Genesis[localConfig.Network].SeedNodes, ",") + if len(tconfig.P2P.Seeds) < 8 && !localConfig.IsSeedNode { + if seeds, ok := config.DefaultSeedNodes[localConfig.Network]; ok { + tconfig.P2P.Seeds = strings.Join(seeds, ",") + } } if len(tconfig.P2P.Seeds) > 0 { log.Infof("seed nodes: %s", tconfig.P2P.Seeds) @@ -149,8 +149,8 @@ func newTendermint(app *BaseApplication, } // if also no Seeds specified, fallback to genesis - if _, ok := vocdoniGenesis.Genesis[localConfig.Network]; ok { - return replacePorts(vocdoniGenesis.Genesis[localConfig.Network].SeedNodes) + if seeds, ok := config.DefaultSeedNodes[localConfig.Network]; ok { + return replacePorts(seeds) } return nil @@ -167,8 +167,8 @@ func newTendermint(app *BaseApplication, tconfig.StateSync.TrustHeight = localConfig.StateSyncTrustHeight tconfig.StateSync.TrustHash = localConfig.StateSyncTrustHash - // If StateSync is enabled but parameters are empty, try our best to populate them - // first try to fetch params from remote API endpoint + // If StateSync is enabled but parameters are empty, populate them + // fetching params from remote API endpoint if localConfig.StateSyncFetchParamsFromRPC && tconfig.StateSync.TrustHeight == 0 && tconfig.StateSync.TrustHash == "" { tconfig.StateSync.TrustHeight, tconfig.StateSync.TrustHash = func() (int64, string) { @@ -196,18 +196,6 @@ func newTendermint(app *BaseApplication, return status.SyncInfo.LatestBlockHeight, status.SyncInfo.LatestBlockHash.String() }() } - - // if still empty, fallback to hardcoded params, if defined for the current network & chainID - if tconfig.StateSync.TrustHeight == 0 && tconfig.StateSync.TrustHash == "" { - if g, ok := vocdoniGenesis.Genesis[localConfig.Network]; ok { - if statesync, ok := g.StateSync[g.Genesis.ChainID]; ok { - tconfig.StateSync.TrustHeight = statesync.TrustHeight - tconfig.StateSync.TrustHash = statesync.TrustHash.String() - log.Infow("using hardcoded statesync params", - "height", tconfig.StateSync.TrustHeight, "hash", tconfig.StateSync.TrustHash) - } - } - } } tconfig.RPC.ListenAddress = "tcp://0.0.0.0:26657" @@ -215,13 +203,25 @@ func newTendermint(app *BaseApplication, // tmdbBackend defaults to goleveldb, but switches to cleveldb if // -tags=cleveldb is used. See tmdb_*.go. tconfig.DBBackend = string(tmdbBackend) - if localConfig.Genesis != "" { - tconfig.Genesis = localConfig.Genesis + tconfig.Genesis = localConfig.Genesis + + if _, err := os.Stat(tconfig.Genesis); os.IsNotExist(err) { + log.Infof("writing hardcoded comet genesis to %s", tconfig.Genesis) + if err := genesis.HardcodedForNetwork(localConfig.Network).SaveAs(tconfig.Genesis); err != nil { + return nil, fmt.Errorf("cannot write genesis file: %w", err) + } } - if err := tconfig.ValidateBasic(); err != nil { - return nil, fmt.Errorf("config is invalid: %w", err) + // We need to load the genesis already, + // to fetch chain_id in order to make Replay work, since signatures depend on it. + // and also to check InitialHeight to decide what to reply when cometbft ask for Info() + loadedGenesis, err := genesis.LoadFromFile(tconfig.GenesisFile()) + if err != nil { + return nil, fmt.Errorf("cannot load genesis file: %w", err) } + log.Infow("loaded genesis file", "genesis", tconfig.GenesisFile(), "chainID", loadedGenesis.ChainID) + app.genesisDoc = loadedGenesis + app.SetChainID(loadedGenesis.ChainID) // read or create local private validator pv, err := NewPrivateValidator( @@ -256,25 +256,6 @@ func newTendermint(app *BaseApplication, return nil, fmt.Errorf("cannot create or load node key: %w", err) } } - log.Infow("vochain initialized", - "db-backend", tconfig.DBBackend, - "publicKey", hex.EncodeToString(pv.Key.PubKey.Bytes()), - "accountAddr", app.NodeAddress, - "validatorAddr", pv.Key.PubKey.Address(), - "external-address", tconfig.P2P.ExternalAddress, - "nodeId", nodeKey.ID(), - "seed", tconfig.P2P.SeedMode) - - // read or create genesis file - if _, err := os.Stat(tconfig.GenesisFile()); err == nil { - log.Infof("found genesis file %s", tconfig.GenesisFile()) - } else { - log.Debugf("loaded genesis: %s", string(genesis)) - if err := os.WriteFile(tconfig.GenesisFile(), genesis, 0o600); err != nil { - return nil, err - } - log.Infof("new genesis created, stored at %s", tconfig.GenesisFile()) - } if localConfig.TendermintMetrics { tconfig.Instrumentation = &cometconfig.InstrumentationConfig{ @@ -285,25 +266,22 @@ func newTendermint(app *BaseApplication, } } - // We need to fetch chain_id in order to make Replay work, - // since signatures depend on it. - type genesisChainID struct { - ChainID string `json:"chain_id"` - } - genesisData, err := os.ReadFile(tconfig.GenesisFile()) - if err != nil { - return nil, fmt.Errorf("cannot read genesis file: %w", err) - } - genesisCID := &genesisChainID{} - if err := json.Unmarshal(genesisData, genesisCID); err != nil { - return nil, fmt.Errorf("cannot unmarshal genesis file for fetching chainID") + if err := tconfig.ValidateBasic(); err != nil { + return nil, fmt.Errorf("config is invalid: %w", err) } - log.Infow("genesis file", "genesis", tconfig.GenesisFile(), "chainID", genesisCID.ChainID) - app.SetChainID(genesisCID.ChainID) + + log.Infow("vochain initialized", + "db-backend", tconfig.DBBackend, + "publicKey", hex.EncodeToString(pv.Key.PubKey.Bytes()), + "accountAddr", app.NodeAddress, + "validatorAddr", pv.Key.PubKey.Address(), + "external-address", tconfig.P2P.ExternalAddress, + "nodeId", nodeKey.ID(), + "seed", tconfig.P2P.SeedMode) // the chain might need additional ZkCircuits, now that we know the chainID ensure they are downloaded now, // to avoid delays at beginBlock during a fork - if err := circuit.DownloadArtifactsForChainID(genesisCID.ChainID); err != nil { + if err := circuit.DownloadArtifactsForChainID(app.ChainID()); err != nil { return nil, fmt.Errorf("cannot download zk circuits for chainID: %w", err) }