github.com/klaytn/klaytn@v1.12.1/cmd/abigen/main.go (about)

     1  // Modifications Copyright 2018 The klaytn Authors
     2  // Copyright 2019 The go-ethereum Authors
     3  // This file is part of go-ethereum.
     4  //
     5  // go-ethereum is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // go-ethereum is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    13  // GNU General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU General Public License
    16  // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
    17  //
    18  // This file is derived from cmd/abigen/main.go (2018/06/04).
    19  // Modified and improved for the klaytn development.
    20  
    21  package main
    22  
    23  import (
    24  	"encoding/json"
    25  	"fmt"
    26  	"io"
    27  	"os"
    28  	"regexp"
    29  	"strings"
    30  
    31  	"github.com/klaytn/klaytn/accounts/abi/bind"
    32  	"github.com/klaytn/klaytn/cmd/utils"
    33  	"github.com/klaytn/klaytn/common/compiler"
    34  	"github.com/klaytn/klaytn/crypto"
    35  	"github.com/klaytn/klaytn/log"
    36  	"github.com/urfave/cli/v2"
    37  )
    38  
    39  const (
    40  	commandHelperTemplate = `{{.Name}}{{if .Subcommands}} command{{end}}{{if .Flags}} [command options]{{end}} [arguments...]
    41  {{if .Description}}{{.Description}}
    42  {{end}}{{if .Subcommands}}
    43  SUBCOMMANDS:
    44  	{{range .Subcommands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}
    45  	{{end}}{{end}}{{if .Flags}}
    46  OPTIONS:
    47  {{range $.Flags}}{{"\t"}}{{.}}
    48  {{end}}
    49  {{end}}`
    50  )
    51  
    52  var (
    53  	// Git SHA1 commit hash of the release (set via linker flags)
    54  	gitCommit = ""
    55  
    56  	app *cli.App
    57  
    58  	// Flags needed by abigen
    59  	abiFlag = &cli.StringFlag{
    60  		Name:  "abi",
    61  		Usage: "Path to the Klaytn contract ABI json to bind, - for STDIN",
    62  	}
    63  	binFlag = &cli.StringFlag{
    64  		Name:  "bin",
    65  		Usage: "Path to the Klaytn contract bytecode (generate deploy method)",
    66  	}
    67  	binruntimeFlag = &cli.StringFlag{
    68  		Name:  "binruntime",
    69  		Usage: "Path to the GXP contract runtime-bytecode",
    70  	}
    71  	typeFlag = &cli.StringFlag{
    72  		Name:  "type",
    73  		Usage: "Struct name for the binding (default = package name)",
    74  	}
    75  	jsonFlag = &cli.StringFlag{
    76  		Name:  "combined-json",
    77  		Usage: "Path to the combined-json file generated by compiler",
    78  	}
    79  	solFlag = &cli.StringFlag{
    80  		Name:  "sol",
    81  		Usage: "Path to the Klaytn contract Solidity source to build and bind",
    82  	}
    83  	solcFlag = &cli.StringFlag{
    84  		Name:  "solc",
    85  		Usage: "Solidity compiler to use if source builds are requested",
    86  		Value: "solc",
    87  	}
    88  	excFlag = &cli.StringFlag{
    89  		Name:  "exc",
    90  		Usage: "Comma separated types to exclude from binding",
    91  	}
    92  	pkgFlag = &cli.StringFlag{
    93  		Name:  "pkg",
    94  		Usage: "Package name to generate the binding into",
    95  	}
    96  	outFlag = &cli.StringFlag{
    97  		Name:  "out",
    98  		Usage: "Output file for the generated binding (default = stdout)",
    99  	}
   100  	langFlag = &cli.StringFlag{
   101  		Name:  "lang",
   102  		Usage: "Destination language for the bindings (go, java, objc)",
   103  		Value: "go",
   104  	}
   105  	aliasFlag = &cli.StringFlag{
   106  		Name:  "alias",
   107  		Usage: "Comma separated aliases for function and event renaming, e.g. foo=bar",
   108  	}
   109  )
   110  
   111  func init() {
   112  	app = utils.NewApp(gitCommit, "klaytn checkpoint helper tool")
   113  	app.Flags = []cli.Flag{
   114  		abiFlag,
   115  		binFlag,
   116  		binruntimeFlag,
   117  		typeFlag,
   118  		jsonFlag,
   119  		solFlag,
   120  		solcFlag,
   121  		excFlag,
   122  		pkgFlag,
   123  		outFlag,
   124  		langFlag,
   125  		aliasFlag,
   126  	}
   127  	app.Action = abigen
   128  	cli.CommandHelpTemplate = commandHelperTemplate
   129  }
   130  
   131  func abigen(c *cli.Context) error {
   132  	utils.CheckExclusive(c, abiFlag, jsonFlag, solFlag) // Only one source can be selected.
   133  	if c.String(pkgFlag.Name) == "" {
   134  		log.Fatalf("No destination package specified (--pkg)")
   135  	}
   136  	var lang bind.Lang
   137  	switch c.String(langFlag.Name) {
   138  	case "go":
   139  		lang = bind.LangGo
   140  	case "java":
   141  		lang = bind.LangJava
   142  	case "objc":
   143  		lang = bind.LangObjC
   144  		log.Fatalf("Objc binding generation is uncompleted")
   145  	default:
   146  		log.Fatalf("Unsupported destination language \"%s\" (--lang)", c.String(langFlag.Name))
   147  	}
   148  	// If the entire solidity code was specified, build and bind based on that
   149  	var (
   150  		abis        []string
   151  		bins        []string
   152  		binruntimes []string
   153  		types       []string
   154  		sigs        []map[string]string
   155  		libs        = make(map[string]string)
   156  		aliases     = make(map[string]string)
   157  	)
   158  	if c.String(abiFlag.Name) != "" {
   159  		// Load up the ABI, optional bytecode and type name from the parameters
   160  		var (
   161  			abi []byte
   162  			err error
   163  		)
   164  		input := c.String(abiFlag.Name)
   165  		if input == "-" {
   166  			abi, err = io.ReadAll(os.Stdin)
   167  		} else {
   168  			abi, err = os.ReadFile(input)
   169  		}
   170  		if err != nil {
   171  			log.Fatalf("Failed to read input ABI: %v", err)
   172  		}
   173  		abis = append(abis, string(abi))
   174  
   175  		var bin []byte
   176  		if binFile := c.String(binFlag.Name); binFile != "" {
   177  			if bin, err = os.ReadFile(binFile); err != nil {
   178  				log.Fatalf("Failed to read input bytecode: %v", err)
   179  			}
   180  			if strings.Contains(string(bin), "//") {
   181  				log.Fatalf("Contract has additional library references, please use other mode(e.g. --combined-json) to catch library infos")
   182  			}
   183  		}
   184  		bins = append(bins, string(bin))
   185  		var binruntime []byte
   186  		if binruntimeFile := c.String(binruntimeFlag.Name); binruntimeFile != "" {
   187  			if binruntime, err = os.ReadFile(binruntimeFile); err != nil {
   188  				log.Fatalf("Failed to read input runtime-bytecode: %v", err)
   189  			}
   190  			if strings.Contains(string(binruntime), "//") {
   191  				log.Fatalf("Contract has additional library references, please use other ")
   192  			}
   193  		}
   194  		binruntimes = append(binruntimes, string(binruntime))
   195  
   196  		kind := c.String(typeFlag.Name)
   197  		if kind == "" {
   198  			kind = c.String(pkgFlag.Name)
   199  		}
   200  		types = append(types, kind)
   201  	} else {
   202  		// Generate the list of types to exclude from binding
   203  		exclude := make(map[string]bool)
   204  		for _, kind := range strings.Split(c.String(excFlag.Name), ",") {
   205  			exclude[strings.ToLower(kind)] = true
   206  		}
   207  		var err error
   208  		var contracts map[string]*compiler.Contract
   209  
   210  		switch {
   211  		case c.IsSet(solFlag.Name):
   212  			contracts, err = compiler.CompileSolidity(c.String(solcFlag.Name), c.String(solFlag.Name))
   213  			if err != nil {
   214  				log.Fatalf("Failed to build Solidity contract: %v", err)
   215  			}
   216  		case c.IsSet(jsonFlag.Name):
   217  			jsonOutput, err := os.ReadFile(c.String(jsonFlag.Name))
   218  			if err != nil {
   219  				log.Fatalf("Failed to read combined-json from compiler: %v", err)
   220  			}
   221  			contracts, err = compiler.ParseCombinedJSON(jsonOutput, "", "", "", "")
   222  			if err != nil {
   223  				log.Fatalf("Failed to read contract information from json output: %v", err)
   224  			}
   225  		}
   226  		// Gather all non-excluded contract for binding
   227  		for name, contract := range contracts {
   228  			if exclude[strings.ToLower(name)] {
   229  				continue
   230  			}
   231  			abi, err := json.Marshal(contract.Info.AbiDefinition) // Flatten the compiler parse
   232  			if err != nil {
   233  				log.Fatalf("Failed to parse ABIs from compiler output: %v", err)
   234  			}
   235  			abis = append(abis, string(abi))
   236  			bins = append(bins, contract.Code)
   237  			binruntimes = append(binruntimes, contract.RuntimeCode)
   238  			sigs = append(sigs, contract.Hashes)
   239  			nameParts := strings.Split(name, ":")
   240  			types = append(types, nameParts[len(nameParts)-1])
   241  
   242  			libPattern := crypto.Keccak256Hash([]byte(name)).String()[2:36]
   243  			libs[libPattern] = nameParts[len(nameParts)-1]
   244  		}
   245  	}
   246  	// Extract all aliases from the flags
   247  	if c.IsSet(aliasFlag.Name) {
   248  		// We support multi-versions for aliasing
   249  		// e.g.
   250  		//      foo=bar,foo2=bar2
   251  		//      foo:bar,foo2:bar2
   252  		re := regexp.MustCompile(`(?:(\w+)[:=](\w+))`)
   253  		submatches := re.FindAllStringSubmatch(c.String(aliasFlag.Name), -1)
   254  		for _, match := range submatches {
   255  			aliases[match[1]] = match[2]
   256  		}
   257  	}
   258  	// Generate the contract binding
   259  	code, err := bind.Bind(types, abis, bins, binruntimes, sigs, c.String(pkgFlag.Name), lang, libs, aliases)
   260  	if err != nil {
   261  		log.Fatalf("Failed to generate ABI binding: %v", err)
   262  	}
   263  	// Either flush it out to a file or display on the standard output
   264  	if !c.IsSet(outFlag.Name) {
   265  		fmt.Printf("%s\n", code)
   266  		return nil
   267  	}
   268  	if err := os.WriteFile(c.String(outFlag.Name), []byte(code), 0o600); err != nil {
   269  		log.Fatalf("Failed to write ABI binding: %v", err)
   270  	}
   271  	return nil
   272  }
   273  
   274  func main() {
   275  	log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
   276  
   277  	if err := app.Run(os.Args); err != nil {
   278  		fmt.Fprintln(os.Stderr, err)
   279  		os.Exit(1)
   280  	}
   281  }