diff --git a/cmd/aergoluac/main.go b/cmd/aergoluac/main.go index d305fd093..1c4c120b3 100644 --- a/cmd/aergoluac/main.go +++ b/cmd/aergoluac/main.go @@ -17,6 +17,7 @@ var ( rootCmd *cobra.Command abiFile string payload bool + decode bool version bool ) @@ -24,7 +25,7 @@ var githash = "No git hash provided" func init() { rootCmd = &cobra.Command{ - Use: "aergoluac --payload srcfile\n aergoluac --abi abifile srcfile bcfile", + Use: "aergoluac --payload srcfile\n aergoluac srcfile bcfile\n aergoluac --abi abifile srcfile bcfile\n aergoluac --decode payloadfile", Short: "Compile a lua contract", Long: "Compile a lua contract. This command makes a bytecode file and a ABI file or prints a payload data.", RunE: func(cmd *cobra.Command, args []string) error { @@ -34,7 +35,14 @@ func init() { cmd.Printf("Aergoluac %s\n", githash) return nil } - if payload { + + if decode { + if len(args) == 0 { + err = util.DecodeFromStdin() + } else { + err = util.DecodeFromFile(args[0]) + } + } else if payload { if len(args) == 0 { err = util.DumpFromStdin() } else { @@ -46,11 +54,13 @@ func init() { } err = util.CompileFromFile(args[0], args[1], abiFile) } + return err }, } rootCmd.PersistentFlags().StringVarP(&abiFile, "abi", "a", "", "abi filename") rootCmd.PersistentFlags().BoolVar(&payload, "payload", false, "print the compilation result consisting of bytecode and abi") + rootCmd.PersistentFlags().BoolVar(&decode, "decode", false, "extract raw bytecode and abi from payload (hex or base58)") rootCmd.PersistentFlags().BoolVar(&version, "version", false, "print the version number of aergoluac") } diff --git a/cmd/aergoluac/util/luac_util.go b/cmd/aergoluac/util/luac_util.go index 20b038260..8fc03ab01 100644 --- a/cmd/aergoluac/util/luac_util.go +++ b/cmd/aergoluac/util/luac_util.go @@ -20,6 +20,8 @@ import ( "runtime" "unsafe" + "github.com/aergoio/aergo/v2/internal/enc/hex" + "github.com/aergoio/aergo/v2/internal/enc/base58" "github.com/aergoio/aergo/v2/cmd/aergoluac/encoding" ) @@ -130,6 +132,112 @@ func dumpToBytes(L *C.lua_State) LuaCode { return NewLuaCode(C.GoBytes(unsafe.Pointer(c), C.int(lc)), C.GoBytes(unsafe.Pointer(a), C.int(la))) } +func isHexString(s string) bool { + // check is the input has even number of characters + if len(s)%2 != 0 { + return false + } + // check if the input contains only hex characters + for _, c := range s { + if !((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) { + return false + } + } + return true +} + +func Decode(srcFileName string, payload string) error { + + var decoded []byte + var err error + + // check if the payload is in hex format + if isHexString(payload) { + // the data is expected to be copied from aergoscan view of + // the transaction that deployed the contract + decoded, err = hex.Decode(payload) + } else { + // the data is the output of aergoluac + decoded, err = encoding.DecodeCode(payload) + if err != nil { + // the data is extracted from JSON transaction from aergocli + decoded, err = base58.Decode(payload) + } + } + if err != nil { + return fmt.Errorf("failed to decode payload 1: %v", err.Error()) + } + + data := LuaCodePayload(decoded) + _, err = data.IsValidFormat() + if err != nil { + return fmt.Errorf("failed to decode payload 2: %v", err.Error()) + } + + contract := data.Code() + if !contract.IsValidFormat() { + // the data is the output of aergoluac, so it does not contain deploy arguments + contract = LuaCode(decoded) + data = NewLuaCodePayload(contract, []byte{}) + } + + err = os.WriteFile(srcFileName + "-bytecode", contract.ByteCode(), 0644); + if err != nil { + return fmt.Errorf("failed to write bytecode file: %v", err.Error()) + } + + err = os.WriteFile(srcFileName + "-abi", contract.ABI(), 0644); + if err != nil { + return fmt.Errorf("failed to write ABI file: %v", err.Error()) + } + + var deployArgs []byte + if data.HasArgs() { + deployArgs = data.Args() + } + err = os.WriteFile(srcFileName + "-deploy-arguments", deployArgs, 0644); + if err != nil { + return fmt.Errorf("failed to write deploy-arguments file: %v", err.Error()) + } + + fmt.Println("done.") + return nil +} + +func DecodeFromFile(srcFileName string) error { + payload, err := os.ReadFile(srcFileName) + if err != nil { + return fmt.Errorf("failed to read payload file: %v", err.Error()) + } + return Decode(srcFileName, string(payload)) +} + +func DecodeFromStdin() error { + fi, err := os.Stdin.Stat() + if err != nil { + return err + } + var buf []byte + if (fi.Mode() & os.ModeCharDevice) == 0 { + buf, err = ioutil.ReadAll(os.Stdin) + if err != nil { + return err + } + } else { + var bBuf bytes.Buffer + scanner := bufio.NewScanner(os.Stdin) + for scanner.Scan() { + bBuf.WriteString(scanner.Text() + "\n") + } + if err = scanner.Err(); err != nil { + return err + } + buf = bBuf.Bytes() + } + return Decode("contract", string(buf)) +} + + type LuaCode []byte const byteCodeLenLen = 4