diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go index db29a87e94..5adce02347 100644 --- a/cmd/geth/chaincmd.go +++ b/cmd/geth/chaincmd.go @@ -21,6 +21,7 @@ import ( "fmt" "io" "os" + "reflect" "runtime" "strconv" "sync/atomic" @@ -56,6 +57,20 @@ The init command initializes a new genesis block and definition for the network. This is a destructive action and changes the network in which you will be participating. +It expects the genesis file as argument.`, + } + updateCommand = cli.Command{ + Action: utils.MigrateFlags(updateTransitions), + Name: "update", + Usage: "Update genesis block with new transitions", + ArgsUsage: "<genesisPath>", + Flags: []cli.Flag{ + utils.DataDirFlag, + }, + Category: "BLOCKCHAIN COMMANDS", + Description: ` +The update commands updates the chain data configuration in genesis block for new transition changes. + It expects the genesis file as argument.`, } dumpGenesisCommand = cli.Command{ @@ -206,6 +221,64 @@ func getIsQuorum(file io.Reader) bool { return altGenesis.Config.IsQuorum == nil || *altGenesis.Config.IsQuorum } +// updateTransitions will update genesis block with the new transitions data +func updateTransitions(ctx *cli.Context) error { + // Open and initialise both full and light databases + stack, _ := makeConfigNode(ctx) + defer stack.Close() + + // Make sure we have a valid genesis JSON + genesisPath := ctx.Args().First() + if len(genesisPath) == 0 { + utils.Fatalf("Must supply path to genesis JSON file") + } + file, err := os.Open(genesisPath) + if err != nil { + utils.Fatalf("Failed to read genesis file: %v", err) + } + defer file.Close() + + genesis := new(core.Genesis) + if err := json.NewDecoder(file).Decode(genesis); err != nil { + utils.Fatalf("invalid genesis file: %v", err) + } + + // Quorum + file.Seek(0, 0) + genesis.Config.IsQuorum = getIsQuorum(file) + + if genesis.Config.IsQuorum { + err = genesis.Config.CheckTransitionsData() + if err != nil { + utils.Fatalf("transitions data invalid: %v", err) + } + } else { + return fmt.Errorf("update transitions only apply to quorum configuration") + } + + // Update transitions and recommit to db + for _, name := range []string{"chaindata", "lightchaindata"} { + chaindb, err := stack.OpenDatabase(name, 0, 0, "", false) + if err != nil { + utils.Fatalf("Failed to open database: %v", err) + } + stored := rawdb.ReadCanonicalHash(chaindb, 0) + storedcfg := rawdb.ReadChainConfig(chaindb, stored) + if storedcfg == nil { + return fmt.Errorf("found genesis block without chain config") + } + // Check that new transitions have changed before updating them + if !reflect.DeepEqual(storedcfg.Transitions, genesis.Config.Transitions) { + log.Info("Change found in transitions, proceeding to update chain config") + storedcfg.Transitions = genesis.Config.Transitions + rawdb.WriteChainConfig(chaindb, stored, storedcfg) + } else { + log.Info("No change in transitions, no update required to chain config") + } + } + return nil +} + // initGenesis will initialise the given JSON format genesis file and writes it as // the zero'd block (i.e. genesis) or will fail hard if it can't succeed. func initGenesis(ctx *cli.Context) error { diff --git a/cmd/geth/main.go b/cmd/geth/main.go index f7a65e3e4e..003124a5a9 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -269,6 +269,7 @@ func init() { app.Commands = []cli.Command{ // See chaincmd.go: initCommand, + updateCommand, mpsdbUpgradeCommand, importCommand, exportCommand, diff --git a/go.mod b/go.mod index ace412f412..3336c1e081 100644 --- a/go.mod +++ b/go.mod @@ -89,6 +89,7 @@ require ( golang.org/x/text v0.3.7 golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 google.golang.org/grpc v1.46.0 + google.golang.org/protobuf v1.28.0 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c gopkg.in/karalabe/cookiejar.v2 v2.0.0-20150724131613-8dcd6a7f4951 gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce