main.go 3.58 KB
Newer Older
1 2 3 4 5
package main

import (
	"encoding/hex"
	"fmt"
6
	"log/slog"
7 8 9 10 11 12 13 14 15 16
	"os"

	"github.com/mattn/go-isatty"
	"github.com/urfave/cli/v2"

	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/log"
	"github.com/ethereum/go-ethereum/params"

	opservice "github.com/ethereum-optimism/optimism/op-service"
17
	oplog "github.com/ethereum-optimism/optimism/op-service/log"
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
)

const EnvPrefix = "OP_CHAIN_OPS_PROTOCOL_VERSION"

var (
	MajorFlag = &cli.Uint64Flag{
		Name:    "major",
		Value:   0,
		Usage:   "major version component",
		EnvVars: opservice.PrefixEnvVar(EnvPrefix, "MAJOR"),
	}
	MinorFlag = &cli.Uint64Flag{
		Name:    "minor",
		Value:   0,
		Usage:   "minor version component",
		EnvVars: opservice.PrefixEnvVar(EnvPrefix, "MINOR"),
	}
	PatchFlag = &cli.Uint64Flag{
		Name:    "patch",
		Value:   0,
		Usage:   "patch version component",
		EnvVars: opservice.PrefixEnvVar(EnvPrefix, "PATCH"),
	}
	PrereleaseFlag = &cli.Uint64Flag{
		Name:    "prerelease",
		Value:   0,
		Usage:   "prerelease version component",
		EnvVars: opservice.PrefixEnvVar(EnvPrefix, "PRERELEASE"),
	}
	BuildFlag = &cli.StringFlag{
		Name:    "build",
		Value:   "0000000000000000",
		Usage:   "build version component as 8-byte hex string without 0x prefix",
		EnvVars: opservice.PrefixEnvVar(EnvPrefix, "BUILD"),
	}
)

func main() {
56 57
	color := isatty.IsTerminal(os.Stderr.Fd())
	oplog.SetGlobalLogHandler(log.NewTerminalHandlerWithLevel(os.Stdout, slog.LevelDebug, color))
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142

	app := &cli.App{
		Name:   "protocol-version",
		Usage:  "Util to interact with protocol-version data",
		Flags:  []cli.Flag{},
		Writer: os.Stdout,
	}
	app.Commands = []*cli.Command{
		{
			Name:  "encode",
			Usage: "Encode a protocol version (type 0) to its onchain bytes32 form.",
			Flags: []cli.Flag{
				MajorFlag, MinorFlag, PatchFlag, PrereleaseFlag, BuildFlag,
			},
			Action: encodeProtocolVersion,
		},
		{
			Name:      "decode",
			Usage:     "Decode a protocol version from its onchain bytes32 form (incl. 0x prefix).",
			ArgsUsage: "<bytes32>",
			Action:    decodeProtocolVersion,
		},
	}

	if err := app.Run(os.Args); err != nil {
		log.Crit("critical error", "err", err)
	}
}

func encodeProtocolVersion(ctx *cli.Context) error {
	u32Flag := func(name string) (uint32, error) {
		v := ctx.Uint64(name)
		if v >= uint64(1<<32) {
			return 0, fmt.Errorf("value of flag %q must be valid uint32, %d (0x%x)", name, v, v)
		}
		return uint32(v), nil
	}
	major, err := u32Flag(MajorFlag.Name)
	if err != nil {
		return err
	}
	minor, err := u32Flag(MinorFlag.Name)
	if err != nil {
		return err
	}
	patch, err := u32Flag(PatchFlag.Name)
	if err != nil {
		return err
	}
	prerelease, err := u32Flag(PrereleaseFlag.Name)
	if err != nil {
		return err
	}
	buildVal := ctx.String(BuildFlag.Name)
	if len(buildVal) != 16 {
		return fmt.Errorf("build flag value must be 16 characters")
	}
	var build [8]byte
	if _, err := hex.Decode(build[:], []byte(buildVal)); err != nil {
		return fmt.Errorf("failed to decode build flag: %q", buildVal)
	}

	version := params.ProtocolVersionV0{
		Build:      build,
		Major:      major,
		Minor:      minor,
		Patch:      patch,
		PreRelease: prerelease,
	}.Encode()
	_, err = fmt.Fprintln(ctx.App.Writer, common.Hash(version).String())
	return err
}

func decodeProtocolVersion(ctx *cli.Context) error {
	if ctx.NArg() != 1 {
		return fmt.Errorf("expected 1 argument: protocol version")
	}
	hexVersion := ctx.Args().Get(0)
	var v params.ProtocolVersion
	if err := v.UnmarshalText([]byte(hexVersion)); err != nil {
		return fmt.Errorf("failed to decode protocol version: %w", err)
	}
	_, err := fmt.Fprintln(ctx.App.Writer, v.String())
	return err
}