From 057a7dd780272e3400c871e4069520fdbc406bfd 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 | 171 ++++++++++++++++-------------------- cmd/utils/flags.go | 16 ++-- common/compiler/solidity.go | 13 +-- 3 files changed, 90 insertions(+), 110 deletions(-) diff --git a/cmd/abigen/main.go b/cmd/abigen/main.go index 1c4a2aa069..6bd6efb4c2 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,72 +128,30 @@ 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) == "-" { - // 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 + 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) } - - var contracts map[string]*compiler.Contract - var err error - - switch { - case c.String(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) - } - case c.String(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) - } - default: - contracts, err = contractsFromStdin() - if err != nil { - fmt.Printf("Failed to read input ABIs from STDIN: %v\n", err) - os.Exit(-1) - } - } - - // Gather all non-excluded contract for binding - for name, contract := range contracts { - if exclude[strings.ToLower(name)] { - continue - } - 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) - } - 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) + utils.Fatalf("Failed to read input ABI: %v", err) } 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) + 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)) @@ -214,21 +161,67 @@ func abigen(c *cli.Context) error { 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 err error + var contracts map[string]*compiler.Contract + + switch { + case c.IsSet(solFlag.Name): + contracts, err = compiler.CompileSolidity(c.String(solcFlag.Name), c.String(solFlag.Name)) + if err != nil { + utils.Fatalf("Failed to build Solidity contract: %v", err) + } + case c.IsSet(vyFlag.Name): + contracts, err = compiler.CompileVyper(c.String(vyperFlag.Name), c.String(vyFlag.Name)) + if err != nil { + utils.Fatalf("Failed to build Vyper contract: %v", err) + } + case c.IsSet(jsonFlag.Name): + jsonOutput, err := os.ReadFile(c.String(jsonFlag.Name)) + if err != nil { + 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)] { + continue + } + abi, err := json.Marshal(contract.Info.AbiDefinition) // Flatten the compiler parse + if err != nil { + 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] + } } // 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 9c25f50df4..64dc4b0500 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 56e01ee334..16b91bf747 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,