// Copyright 2016 The go-ethereum Authors // This file is part of go-ethereum. // // go-ethereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // go-ethereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with go-ethereum. If not, see . package geth import ( "fmt" "os" "path/filepath" "strconv" "testing" ) var customGenesisTests = []struct { genesis string query string result string }{ // Genesis file with an empty chain configuration (ensure missing fields work) { genesis: `{ "alloc" : {}, "coinbase" : "0x0000000000000000000000000000000000000000", "difficulty" : "0x20000", "extraData" : "", "gasLimit" : "0x2fefd8", "nonce" : "0x0000000000001338", "mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000", "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", "timestamp" : "0x00", "config" : { "terminalTotalDifficultyPassed": true } }`, query: "eth.getBlock(0).nonce", result: "0x0000000000001338", }, // Genesis file with specific chain configurations { genesis: `{ "alloc" : {}, "coinbase" : "0x0000000000000000000000000000000000000000", "difficulty" : "0x20000", "extraData" : "", "gasLimit" : "0x2fefd8", "nonce" : "0x0000000000001339", "mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000", "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", "timestamp" : "0x00", "config" : { "homesteadBlock" : 42, "daoForkBlock" : 141, "daoForkSupport" : true, "terminalTotalDifficultyPassed" : true } }`, query: "eth.getBlock(0).nonce", result: "0x0000000000001339", }, } // Tests that initializing Geth with a custom genesis block and chain definitions // work properly. func TestCustomGenesis(t *testing.T) { t.Parallel() for i, tt := range customGenesisTests { // Create a temporary data directory to use and inspect later datadir := t.TempDir() // Initialize the data directory with the custom genesis block json := filepath.Join(datadir, "genesis.json") if err := os.WriteFile(json, []byte(tt.genesis), 0600); err != nil { t.Fatalf("test %d: failed to write genesis file: %v", i, err) } runGeth(t, "--datadir", datadir, "init", json).WaitExit() // Query the custom genesis block geth := runGeth(t, "--networkid", "1337", "--syncmode=full", "--cache", "16", "--datadir", datadir, "--maxpeers", "0", "--port", "0", "--authrpc.port", "0", "--nodiscover", "--nat", "none", "--ipcdisable", "--exec", tt.query, "console") geth.ExpectRegexp(tt.result) geth.ExpectExit() } } // TestCustomBackend that the backend selection and detection (leveldb vs pebble) works properly. func TestCustomBackend(t *testing.T) { t.Parallel() // Test pebble, but only on 64-bit platforms if strconv.IntSize != 64 { t.Skip("Custom backends are only available on 64-bit platform") } genesis := `{ "alloc" : {}, "coinbase" : "0x0000000000000000000000000000000000000000", "difficulty" : "0x20000", "extraData" : "", "gasLimit" : "0x2fefd8", "nonce" : "0x0000000000001338", "mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000", "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", "timestamp" : "0x00", "config" : { "terminalTotalDifficultyPassed": true } }` type backendTest struct { initArgs []string initExpect string execArgs []string execExpect string } testfunc := func(t *testing.T, tt backendTest) error { // Create a temporary data directory to use and inspect later datadir := t.TempDir() // Initialize the data directory with the custom genesis block json := filepath.Join(datadir, "genesis.json") if err := os.WriteFile(json, []byte(genesis), 0600); err != nil { return fmt.Errorf("failed to write genesis file: %v", err) } { // Init args := append(tt.initArgs, "--datadir", datadir, "init", json) geth := runGeth(t, args...) geth.ExpectRegexp(tt.initExpect) geth.ExpectExit() } { // Exec + query args := append(tt.execArgs, "--networkid", "1337", "--syncmode=full", "--cache", "16", "--datadir", datadir, "--maxpeers", "0", "--port", "0", "--authrpc.port", "0", "--nodiscover", "--nat", "none", "--ipcdisable", "--exec", "eth.getBlock(0).nonce", "console") geth := runGeth(t, args...) geth.ExpectRegexp(tt.execExpect) geth.ExpectExit() } return nil } for i, tt := range []backendTest{ { // When not specified, it should default to pebble execArgs: []string{"--db.engine", "pebble"}, execExpect: "0x0000000000001338", }, { // Explicit leveldb initArgs: []string{"--db.engine", "leveldb"}, execArgs: []string{"--db.engine", "leveldb"}, execExpect: "0x0000000000001338", }, { // Explicit leveldb first, then autodiscover initArgs: []string{"--db.engine", "leveldb"}, execExpect: "0x0000000000001338", }, { // Explicit pebble initArgs: []string{"--db.engine", "pebble"}, execArgs: []string{"--db.engine", "pebble"}, execExpect: "0x0000000000001338", }, { // Explicit pebble, then auto-discover initArgs: []string{"--db.engine", "pebble"}, execExpect: "0x0000000000001338", }, { // Can't start pebble on top of leveldb initArgs: []string{"--db.engine", "leveldb"}, execArgs: []string{"--db.engine", "pebble"}, execExpect: `Fatal: Failed to register the Ethereum service: db.engine choice was pebble but found pre-existing leveldb database in specified data directory`, }, { // Can't start leveldb on top of pebble initArgs: []string{"--db.engine", "pebble"}, execArgs: []string{"--db.engine", "leveldb"}, execExpect: `Fatal: Failed to register the Ethereum service: db.engine choice was leveldb but found pre-existing pebble database in specified data directory`, }, { // Reject invalid backend choice initArgs: []string{"--db.engine", "mssql"}, initExpect: `Fatal: Invalid choice for db.engine 'mssql', allowed 'leveldb' or 'pebble'`, // Since the init fails, this will return the (default) mainnet genesis // block nonce execExpect: `0x0000000000000042`, }, } { if err := testfunc(t, tt); err != nil { t.Fatalf("test %d-leveldb: %v", i, err) } } }