From a1d4edf503f441f04432a8439b8920ea44e55e60 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 13 Dec 2024 16:52:03 +0800 Subject: [PATCH] cmd/abigen: refactor command line interface (#19797) --- cmd/abigen/main.go | 133 ++++++++++++++++-------------------- cmd/utils/flags.go | 16 ++--- common/compiler/solidity.go | 13 ++-- 3 files changed, 71 insertions(+), 91 deletions(-) diff --git a/cmd/abigen/main.go b/cmd/abigen/main.go index 1c4a2aa06980..6bd6efb4c249 100644 --- a/cmd/abigen/main.go +++ b/cmd/abigen/main.go @@ -24,6 +24,7 @@ import ( "strings" "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind" + "github.com/XinFinOrg/XDPoSChain/cmd/utils" "github.com/XinFinOrg/XDPoSChain/common/compiler" "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/internal/flags" @@ -52,6 +53,10 @@ var ( Name: "type", Usage: "Struct name for the binding (default = package name)", } + jsonFlag = &cli.StringFlag{ + Name: "combined-json", + Usage: "Path to the combined-json file generated by compiler", + } solFlag = &cli.StringFlag{ Name: "sol", Usage: "Path to the Ethereum contract Solidity source to build and bind", @@ -96,6 +101,7 @@ func init() { abiFlag, binFlag, typeFlag, + jsonFlag, solFlag, solcFlag, vyFlag, @@ -109,28 +115,11 @@ func init() { } func abigen(c *cli.Context) error { - if c.String(abiFlag.Name) == "" && c.String(solFlag.Name) == "" && c.String(vyFlag.Name) == "" { - fmt.Printf("No contract ABI (--abi), Solidity source (--sol), or Vyper source (--vy) specified\n") - os.Exit(-1) - } else if (c.String(abiFlag.Name) != "" || c.String(binFlag.Name) != "" || c.String(typeFlag.Name) != "") && (c.String(solFlag.Name) != "" || c.String(vyFlag.Name) != "") { - fmt.Printf("Contract ABI (--abi), bytecode (--bin) and type (--type) flags are mutually exclusive with the Solidity (--sol) and Vyper (--vy) flags\n") - os.Exit(-1) - } else if c.String(solFlag.Name) != "" && c.String(vyFlag.Name) == "" { - fmt.Printf("Solidity (--sol) and Vyper (--vy) flags are mutually exclusive\n") - os.Exit(-1) - } + utils.CheckExclusive(c, abiFlag, jsonFlag, solFlag, vyFlag) // Only one source can be selected. if c.String(pkgFlag.Name) == "" { - fmt.Printf("No destination package specified (--pkg)\n") - os.Exit(-1) - } - var lang bind.Lang - switch c.String(langFlag.Name) { - case "go": - lang = bind.LangGo - default: - fmt.Printf("Unsupported destination language \"%s\" (--lang)\n", c.String(langFlag.Name)) - os.Exit(-1) + utils.Fatalf("No destination package specified (--pkg)") } + lang := bind.LangGo // If the entire solidity code was specified, build and bind based on that var ( abis []string @@ -139,37 +128,69 @@ func abigen(c *cli.Context) error { sigs []map[string]string libs = make(map[string]string) ) - if c.String(solFlag.Name) != "" || c.String(vyFlag.Name) != "" || c.String(abiFlag.Name) == "-" { + if c.String(abiFlag.Name) != "" { + // Load up the ABI, optional bytecode and type name from the parameters + var ( + abi []byte + err error + ) + input := c.String(abiFlag.Name) + if input == "-" { + abi, err = io.ReadAll(os.Stdin) + } else { + abi, err = os.ReadFile(input) + } + if err != nil { + utils.Fatalf("Failed to read input ABI: %v", err) + } + abis = append(abis, string(abi)) + + var bin []byte + if binFile := c.String(binFlag.Name); binFile != "" { + if bin, err = os.ReadFile(binFile); err != nil { + utils.Fatalf("Failed to read input bytecode: %v", err) + } + if strings.Contains(string(bin), "//") { + utils.Fatalf("Contract has additional library references, please use other mode(e.g. --combined-json) to catch library infos") + } + } + bins = append(bins, string(bin)) + + kind := c.String(typeFlag.Name) + if kind == "" { + kind = c.String(pkgFlag.Name) + } + types = append(types, kind) + } else { // Generate the list of types to exclude from binding exclude := make(map[string]bool) for _, kind := range strings.Split(c.String(excFlag.Name), ",") { exclude[strings.ToLower(kind)] = true } - - var contracts map[string]*compiler.Contract var err error + var contracts map[string]*compiler.Contract switch { - case c.String(solFlag.Name) != "": + case c.IsSet(solFlag.Name): contracts, err = compiler.CompileSolidity(c.String(solcFlag.Name), c.String(solFlag.Name)) if err != nil { - fmt.Printf("Failed to build Solidity contract: %v\n", err) - os.Exit(-1) + utils.Fatalf("Failed to build Solidity contract: %v", err) } - case c.String(vyFlag.Name) != "": + case c.IsSet(vyFlag.Name): contracts, err = compiler.CompileVyper(c.String(vyperFlag.Name), c.String(vyFlag.Name)) if err != nil { - fmt.Printf("Failed to build Vyper contract: %v\n", err) - os.Exit(-1) + utils.Fatalf("Failed to build Vyper contract: %v", err) } - default: - contracts, err = contractsFromStdin() + case c.IsSet(jsonFlag.Name): + jsonOutput, err := os.ReadFile(c.String(jsonFlag.Name)) if err != nil { - fmt.Printf("Failed to read input ABIs from STDIN: %v\n", err) - os.Exit(-1) + utils.Fatalf("Failed to read combined-json from compiler: %v", err) + } + contracts, err = compiler.ParseCombinedJSON(jsonOutput, "", "", "", "") + if err != nil { + utils.Fatalf("Failed to read contract information from json output: %v", err) } } - // Gather all non-excluded contract for binding for name, contract := range contracts { if exclude[strings.ToLower(name)] { @@ -177,58 +198,30 @@ func abigen(c *cli.Context) error { } abi, err := json.Marshal(contract.Info.AbiDefinition) // Flatten the compiler parse if err != nil { - fmt.Printf("Failed to parse ABIs from compiler output: %v\n", err) - os.Exit(-1) + utils.Fatalf("Failed to parse ABIs from compiler output: %v", err) } abis = append(abis, string(abi)) bins = append(bins, contract.Code) sigs = append(sigs, contract.Hashes) - nameParts := strings.Split(name, ":") types = append(types, nameParts[len(nameParts)-1]) libPattern := crypto.Keccak256Hash([]byte(name)).String()[2:36] libs[libPattern] = nameParts[len(nameParts)-1] } - } else { - // Otherwise load up the ABI, optional bytecode and type name from the parameters - abi, err := os.ReadFile(c.String(abiFlag.Name)) - - if err != nil { - fmt.Printf("Failed to read input ABI: %v\n", err) - os.Exit(-1) - } - abis = append(abis, string(abi)) - - var bin []byte - if c.String(binFlag.Name) != "" { - if bin, err = os.ReadFile(c.String(binFlag.Name)); err != nil { - fmt.Printf("Failed to read input bytecode: %v\n", err) - os.Exit(-1) - } - } - bins = append(bins, string(bin)) - - kind := c.String(typeFlag.Name) - if kind == "" { - kind = c.String(pkgFlag.Name) - } - types = append(types, kind) } // Generate the contract binding code, err := bind.Bind(types, abis, bins, sigs, c.String(pkgFlag.Name), lang, libs) if err != nil { - fmt.Printf("Failed to generate ABI binding: %v\n", err) - os.Exit(-1) + utils.Fatalf("Failed to generate ABI binding: %v", err) } // Either flush it out to a file or display on the standard output - if c.String(outFlag.Name) == "" { + if !c.IsSet(outFlag.Name) { fmt.Printf("%s\n", code) return nil } if err := os.WriteFile(c.String(outFlag.Name), []byte(code), 0600); err != nil { - fmt.Printf("Failed to write ABI binding: %v\n", err) - os.Exit(-1) + utils.Fatalf("Failed to write ABI binding: %v", err) } return nil } @@ -241,11 +234,3 @@ func main() { os.Exit(1) } } - -func contractsFromStdin() (map[string]*compiler.Contract, error) { - bytes, err := io.ReadAll(os.Stdin) - if err != nil { - return nil, err - } - return compiler.ParseCombinedJSON(bytes, "", "", "", "") -} diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 9c25f50df4c0..64dc4b05004b 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -1024,7 +1024,7 @@ func setWS(ctx *cli.Context, cfg *node.Config) { // setIPC creates an IPC path configuration from the set command line flags, // returning an empty string if IPC was explicitly disabled, or the set path. func setIPC(ctx *cli.Context, cfg *node.Config) { - checkExclusive(ctx, IPCDisabledFlag, IPCPathFlag) + CheckExclusive(ctx, IPCDisabledFlag, IPCPathFlag) switch { case ctx.Bool(IPCDisabledFlag.Name): cfg.IPCPath = "" @@ -1034,7 +1034,7 @@ func setIPC(ctx *cli.Context, cfg *node.Config) { } func setPrefix(ctx *cli.Context, cfg *node.Config) { - checkExclusive(ctx, Enable0xPrefixFlag, EnableXDCPrefixFlag) + CheckExclusive(ctx, Enable0xPrefixFlag, EnableXDCPrefixFlag) } // MakeDatabaseHandles raises out the number of allowed file handles per process @@ -1301,10 +1301,10 @@ func setEthash(ctx *cli.Context, cfg *ethconfig.Config) { } } -// checkExclusive verifies that only a single isntance of the provided flags was +// CheckExclusive verifies that only a single isntance of the provided flags was // set by the user. Each flag might optionally be followed by a string type to // specialize it further. -func checkExclusive(ctx *cli.Context, args ...interface{}) { +func CheckExclusive(ctx *cli.Context, args ...interface{}) { set := make([]string, 0, 1) for i := 0; i < len(args); i++ { // Make sure the next argument is a flag and skip if not set @@ -1381,10 +1381,10 @@ func SetXDCXConfig(ctx *cli.Context, cfg *XDCx.Config, XDCDataDir string) { // SetEthConfig applies eth-related command line flags to the config. func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { // Avoid conflicting network flags - checkExclusive(ctx, DeveloperFlag, TestnetFlag, RinkebyFlag) - checkExclusive(ctx, FastSyncFlag, LightModeFlag, SyncModeFlag) - checkExclusive(ctx, LightServFlag, LightModeFlag) - checkExclusive(ctx, LightServFlag, SyncModeFlag, "light") + CheckExclusive(ctx, DeveloperFlag, TestnetFlag, RinkebyFlag) + CheckExclusive(ctx, FastSyncFlag, LightModeFlag, SyncModeFlag) + CheckExclusive(ctx, LightServFlag, LightModeFlag) + CheckExclusive(ctx, LightServFlag, SyncModeFlag, "light") ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) setEtherbase(ctx, ks, cfg) diff --git a/common/compiler/solidity.go b/common/compiler/solidity.go index 56e01ee334ca..16b91bf747f4 100644 --- a/common/compiler/solidity.go +++ b/common/compiler/solidity.go @@ -142,7 +142,6 @@ func ParseCombinedJSON(combinedJSON []byte, source string, languageVersion strin if err := json.Unmarshal(combinedJSON, &output); err != nil { return nil, err } - // Compilation succeeded, assemble and return the contracts. contracts := make(map[string]*Contract) for name, info := range output.Contracts { @@ -151,14 +150,10 @@ func ParseCombinedJSON(combinedJSON []byte, source string, languageVersion strin if err := json.Unmarshal([]byte(info.Abi), &abi); err != nil { return nil, fmt.Errorf("solc: error reading abi definition (%v)", err) } - var userdoc interface{} - if err := json.Unmarshal([]byte(info.Userdoc), &userdoc); err != nil { - return nil, fmt.Errorf("solc: error reading user doc: %v", err) - } - var devdoc interface{} - if err := json.Unmarshal([]byte(info.Devdoc), &devdoc); err != nil { - return nil, fmt.Errorf("solc: error reading dev doc: %v", err) - } + var userdoc, devdoc interface{} + json.Unmarshal([]byte(info.Userdoc), &userdoc) + json.Unmarshal([]byte(info.Devdoc), &devdoc) + contracts[name] = &Contract{ Code: "0x" + info.Bin, RuntimeCode: "0x" + info.BinRuntime,