github.com/ari-anchor/sei-tendermint@v0.0.0-20230519144642-dc826b7b56bb/scripts/confix/confix.go (about) 1 // Program confix applies fixes to a Tendermint TOML configuration file to 2 // update a file created with an older version of Tendermint to a compatible 3 // format for a newer version. 4 package main 5 6 import ( 7 "bytes" 8 "context" 9 "errors" 10 "flag" 11 "fmt" 12 "log" 13 "os" 14 "path/filepath" 15 16 "github.com/creachadair/atomicfile" 17 "github.com/creachadair/tomledit" 18 "github.com/creachadair/tomledit/transform" 19 "github.com/spf13/viper" 20 21 "github.com/ari-anchor/sei-tendermint/config" 22 ) 23 24 func init() { 25 flag.Usage = func() { 26 fmt.Fprintf(os.Stderr, `Usage: %[1]s -config <src> [-out <dst>] 27 28 Modify the contents of the specified -config TOML file to update the names, 29 locations, and values of configuration settings to the current configuration 30 layout. The output is written to -out, or to stdout. 31 32 It is valid to set -config and -out to the same path. In that case, the file will 33 be modified in-place. In case of any error in updating the file, no output is 34 written. 35 36 Options: 37 `, filepath.Base(os.Args[0])) 38 flag.PrintDefaults() 39 } 40 } 41 42 var ( 43 configPath = flag.String("config", "", "Config file path (required)") 44 outPath = flag.String("out", "", "Output file path (default stdout)") 45 ) 46 47 func main() { 48 flag.Parse() 49 if *configPath == "" { 50 log.Fatal("You must specify a non-empty -config path") 51 } 52 53 doc, err := LoadConfig(*configPath) 54 if err != nil { 55 log.Fatalf("Loading config: %v", err) 56 } 57 58 ctx := transform.WithLogWriter(context.Background(), os.Stderr) 59 if err := ApplyFixes(ctx, doc); err != nil { 60 log.Fatalf("Updating %q: %v", *configPath, err) 61 } 62 63 var buf bytes.Buffer 64 if err := tomledit.Format(&buf, doc); err != nil { 65 log.Fatalf("Formatting config: %v", err) 66 } 67 68 // Verify that Tendermint can parse the results after our edits. 69 if err := CheckValid(buf.Bytes()); err != nil { 70 log.Fatalf("Updated config is invalid: %v", err) 71 } 72 73 if *outPath == "" { 74 os.Stdout.Write(buf.Bytes()) 75 } else if err := atomicfile.WriteData(*outPath, buf.Bytes(), 0600); err != nil { 76 log.Fatalf("Writing output: %v", err) 77 } 78 } 79 80 // ApplyFixes transforms doc and reports whether it succeeded. 81 func ApplyFixes(ctx context.Context, doc *tomledit.Document) error { 82 // Check what version of Tendermint might have created this config file, as 83 // a safety check for the updates we are about to make. 84 tmVersion := GuessConfigVersion(doc) 85 if tmVersion == vUnknown { 86 return errors.New("cannot tell what Tendermint version created this config") 87 } else if tmVersion < v34 || tmVersion > v36 { 88 // TODO(creachadair): Add in rewrites for older versions. This will 89 // require some digging to discover what the changes were. The upgrade 90 // instructions do not give specifics. 91 return fmt.Errorf("unable to update version %s config", tmVersion) 92 } 93 return plan.Apply(ctx, doc) 94 } 95 96 // LoadConfig loads and parses the TOML document from path. 97 func LoadConfig(path string) (*tomledit.Document, error) { 98 f, err := os.Open(path) 99 if err != nil { 100 return nil, err 101 } 102 defer f.Close() 103 return tomledit.Parse(f) 104 } 105 106 const ( 107 vUnknown = "" 108 v32 = "v0.32" 109 v33 = "v0.33" 110 v34 = "v0.34" 111 v35 = "v0.35" 112 v36 = "v0.36" 113 ) 114 115 // GuessConfigVersion attempts to figure out which version of Tendermint 116 // created the specified config document. It returns "" if the creating version 117 // cannot be determined, otherwise a string of the form "vX.YY". 118 func GuessConfigVersion(doc *tomledit.Document) string { 119 hasDisableWS := doc.First("rpc", "experimental-disable-websocket") != nil 120 hasUseLegacy := doc.First("p2p", "use-legacy") != nil // v0.35 only 121 if hasDisableWS && !hasUseLegacy { 122 return v36 123 } 124 125 hasBlockSync := transform.FindTable(doc, "blocksync") != nil // add: v0.35 126 hasStateSync := transform.FindTable(doc, "statesync") != nil // add: v0.34 127 if hasBlockSync && hasStateSync { 128 return v35 129 } else if hasStateSync { 130 return v34 131 } 132 133 hasIndexKeys := doc.First("tx_index", "index_keys") != nil // add: v0.33 134 hasIndexTags := doc.First("tx_index", "index_tags") != nil // rem: v0.33 135 if hasIndexKeys && !hasIndexTags { 136 return v33 137 } 138 139 hasFastSync := transform.FindTable(doc, "fastsync") != nil // add: v0.32 140 if hasIndexTags && hasFastSync { 141 return v32 142 } 143 144 // Something older, probably. 145 return vUnknown 146 } 147 148 // CheckValid checks whether the specified config appears to be a valid 149 // Tendermint config file. This emulates how the node loads the config. 150 func CheckValid(data []byte) error { 151 v := viper.New() 152 v.SetConfigType("toml") 153 154 if err := v.ReadConfig(bytes.NewReader(data)); err != nil { 155 return fmt.Errorf("reading config: %w", err) 156 } 157 158 var cfg config.Config 159 if err := v.Unmarshal(&cfg); err != nil { 160 return fmt.Errorf("decoding config: %w", err) 161 } 162 163 return cfg.ValidateBasic() 164 }