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  }