From 61e3b3cf09a412707f73ff398d6cf88cc0138cf9 Mon Sep 17 00:00:00 2001 From: Arran Schlosberg Date: Fri, 10 Jun 2022 11:16:23 +0100 Subject: [PATCH] Add `--experimental_src_map` to `ethier gen` command (#35) * Solidity source mapping from OpCodes at runtime! * Fix test failure introduced by upgrade of go-ethereum + bring new test code in line with implementation variable names. * Run prettier on Solidity test contract * Properly handle source-code resolution from EVM trace when executing libraries. * GitHub test Action is failing despite tests passing so attempting to change the node_modules cache key to clear it. * Revert GitHub Action cache key and change testing to non-verbose as it hides failures; also output `solc` + `abigen` versions. * Deliberate panic in ethier/gen.go to diagnose difference from local run to GitHub Actions * A further deliberate panic in ethier/gen.go to see full output; abigen differs and regexp is a bad idea here because it's too fragile * Update ethier/gen.go to match output of old and new versions of abigen after https://github.com/ethereum/go-ethereum/pull/24835. See the TODO in ethier/gen.go re direct modification of the bind.Bind() template. * Simplify calculation of PUSH instruction offset --- .github/workflows/test.yml | 2 +- eth/signer_test.go | 28 +--- ethier/gen.go | 175 ++++++++++++++++++++- ethier/gen_extra.go.tmpl | 52 +++++++ go.mod | 18 ++- go.sum | 71 +++++++-- solidity/solidity.go | 203 +++++++++++++++++++++++++ solidity/solidity_test.go | 44 ++++++ solidity/source_map_test.go | 168 ++++++++++++++++++++ solidity/srcmaptest/SourceMapTest.sol | 53 +++++++ solidity/srcmaptest/SourceMapTest2.sol | 19 +++ solidity/srcmaptest/srcmaptest.go | 31 ++++ 12 files changed, 812 insertions(+), 52 deletions(-) create mode 100644 ethier/gen_extra.go.tmpl create mode 100644 solidity/solidity.go create mode 100644 solidity/solidity_test.go create mode 100644 solidity/source_map_test.go create mode 100644 solidity/srcmaptest/SourceMapTest.sol create mode 100644 solidity/srcmaptest/SourceMapTest2.sol create mode 100644 solidity/srcmaptest/srcmaptest.go diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9c783ab..4837aa8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -40,4 +40,4 @@ jobs: sudo apt-get install -y solc abigen - name: Run tests - run: npm run test:verbose + run: npm run test diff --git a/eth/signer_test.go b/eth/signer_test.go index ecf3fae..40baa30 100644 --- a/eth/signer_test.go +++ b/eth/signer_test.go @@ -14,6 +14,7 @@ import ( "github.com/google/tink/go/keyset" "github.com/google/tink/go/prf" "github.com/google/tink/go/tink" + "github.com/h-fam/errdiff" // These tests require ethtest.SimulatedBackend but that would result in a // cyclical dependency. As this is limited to these tests and not the @@ -161,19 +162,19 @@ func TestTransactorWithChainID(t *testing.T) { const gasLimit = 21000 txFee := new(big.Int).Mul(gasPrice, big.NewInt(gasLimit)) - sendEth := func(t *testing.T, opts *bind.TransactOpts, to common.Address, value *big.Int) { + sendEth := func(t *testing.T, opts *bind.TransactOpts, to common.Address, value *big.Int, errDiffAgainst interface{}) { t.Helper() unsigned := types.NewTransaction(0, to, value, gasLimit, gasPrice, nil) tx, err := opts.Signer(opts.From, unsigned) if err != nil { t.Fatalf("%T.Signer(%+v) error %v", opts, unsigned, err) } - if err := sim.SendTransaction(ctx, tx); err != nil { - t.Fatalf("%T.SendTransaction() error %v", sim, err) + if diff := errdiff.Check(sim.SendTransaction(ctx, tx), errDiffAgainst); diff != "" { + t.Fatalf("%T.SendTransaction() %s", sim, diff) } } - sendEth(t, sim.Acc(0), signer.Address(), Ether(42)) + sendEth(t, sim.Acc(0), signer.Address(), Ether(42), nil) wantBalance(ctx, t, "faucet after sending 42", sim.Addr(0), new(big.Int).Sub(Ether(100-42), txFee)) wantBalance(ctx, t, "signer after receiving 42", signer.Address(), Ether(42)) @@ -183,32 +184,17 @@ func TestTransactorWithChainID(t *testing.T) { if err != nil { t.Fatalf("%T.TransactorWithChainID(%d) error %v", signer, chainID, err) } - sendEth(t, opts, sim.Addr(0), Ether(21)) + sendEth(t, opts, sim.Addr(0), Ether(21), nil) wantBalance(ctx, t, "faucet after sending 42 and receiving 21", sim.Addr(0), new(big.Int).Sub(Ether(100-42+21), txFee)) wantBalance(ctx, t, "signer after receiving 42 and sending 21", signer.Address(), new(big.Int).Sub(Ether(42-21), txFee)) }) t.Run("incorrect chain ID", func(t *testing.T) { - // The SimulatedBackend panics instead of returning an error when the - // chain ID is incorrect. #java - defer func() { - const wantContains = "invalid chain id" - r := recover() - - if err, ok := r.(error); ok && strings.Contains(err.Error(), wantContains) { - return - } - t.Errorf("%T.SendTransaction(%T.TransactorWithChainID()) recovered %T(%v); want panic with error containing %q", sim, signer, r, r, wantContains) - }() - chainID := new(big.Int).Add(sim.Blockchain().Config().ChainID, big.NewInt(1)) opts, err := signer.TransactorWithChainID(chainID) if err != nil { t.Fatalf("%T.TransactorWithChainID(%d) error %v", signer, chainID, err) } - sendEth(t, opts, sim.Addr(0), Ether(1)) - // We should never reach here because sendEth results in a panic inside - // go-ethereum's SimulatedBackend. - t.Errorf("%T.SendTransaction(%T.TransactorWithChainID()) did not panic", sim, signer) + sendEth(t, opts, sim.Addr(0), Ether(1), "invalid chain id") }) } diff --git a/ethier/gen.go b/ethier/gen.go index cfcee5f..6a68966 100644 --- a/ethier/gen.go +++ b/ethier/gen.go @@ -1,20 +1,33 @@ package main import ( + "bytes" + "encoding/json" "errors" "fmt" + "go/format" + "go/parser" + "go/token" "io" "log" "os" "os/exec" "path/filepath" + "regexp" "strings" + "text/template" + "github.com/ethereum/go-ethereum/common/compiler" "github.com/spf13/cobra" + "golang.org/x/tools/go/ast/astutil" + + _ "embed" ) +const srcMapFlag = "experimental_src_map" + func init() { - rootCmd.AddCommand(&cobra.Command{ + cmd := &cobra.Command{ Use: "gen", Short: "Compiles Solidity contracts to generate Go ABI bindings with go:generate", RunE: gen, @@ -29,12 +42,16 @@ func init() { } return nil }, - }) + } + + cmd.Flags().Bool(srcMapFlag, false, "Generate source maps to determine Solidity code location from EVM traces") + + rootCmd.AddCommand(cmd) } // gen runs `solc | abigen` on the Solidity source files passed as the args. // TODO: support wildcard / glob matching of files. -func gen(_ *cobra.Command, args []string) (retErr error) { +func gen(cmd *cobra.Command, args []string) (retErr error) { pwd, err := os.Getwd() if err != nil { return fmt.Errorf("os.Getwd(): %v", err) @@ -62,7 +79,7 @@ func gen(_ *cobra.Command, args []string) (retErr error) { args, "--base-path", basePath, "--include-path", filepath.Join(basePath, "node_modules"), - "--combined-json", "abi,bin", + "--combined-json", "abi,bin,bin-runtime,hashes,srcmap-runtime", ) solc := exec.Command("solc", args...) solc.Stderr = os.Stderr @@ -75,13 +92,16 @@ func gen(_ *cobra.Command, args []string) (retErr error) { "abigen", "--combined-json", "/dev/stdin", "--pkg", pkg, - "--out", "generated.go", ) abigen.Stderr = os.Stderr r, w := io.Pipe() solc.Stdout = w - abigen.Stdin = r + combinedJSON := bytes.NewBuffer(nil) + abigen.Stdin = io.TeeReader(r, combinedJSON) + + generated := bytes.NewBuffer(nil) + abigen.Stdout = generated if err := solc.Start(); err != nil { return fmt.Errorf("start `solc`: %v", err) @@ -99,5 +119,146 @@ func gen(_ *cobra.Command, args []string) (retErr error) { if err := abigen.Wait(); err != nil { return fmt.Errorf("`abigen` returned: %v", err) } - return r.Close() + if err := r.Close(); err != nil { + return fmt.Errorf("close read-half of pipe from solc to abigen: %v", err) + } + + extend, err := cmd.Flags().GetBool(srcMapFlag) + if err != nil { + return fmt.Errorf("%T.Flags().GetBool(%q): %v", cmd, srcMapFlag, err) + } + if !extend { + return os.WriteFile("generated.go", generated.Bytes(), 0644) + } + + out, err := extendGeneratedCode(generated, combinedJSON) + if err != nil { + return err + } + return os.WriteFile("generated.go", out, 0644) +} + +var ( + //go:embed gen_extra.go.tmpl + extraCode string + + // extraTemplate is the template for use by extendGeneratedCode(). + extraTemplate = template.Must( + template.New("extra"). + Funcs(template.FuncMap{ + "quote": func(s interface{}) string { + return fmt.Sprintf("%q", s) + }, + "stringSlice": func(strs []string) string { + q := make([]string, len(strs)) + for i, s := range strs { + q[i] = fmt.Sprintf("%q", s) + } + return fmt.Sprintf("[]string{%s}", strings.Join(q, ", ")) + }, + "contract": func(s string) (string, error) { + parts := strings.Split(s, ".sol:") + if len(parts) != 2 { + return "", fmt.Errorf("invalid contract name %q must have format path/to/file.sol:ContractName", s) + } + return parts[1], nil + }, + }). + Parse(extraCode), + ) + + // Regular expressions for modifying abigen-generated code to work with the + // extraTemplate code above. + deployedRegexp = regexp.MustCompile(`^\s*return address, tx, &(.+?)\{.*Transactor.*\}, nil\s*$`) + // Note the option for matching strings.Replace or strings.ReplaceAll due to + // a recent change in abigen. + libReplacementRegexp = regexp.MustCompile(`^\s*(.+?)Bin = strings.Replace(?:All)?\(.+?, "__\$([0-9a-f]{34})\$__", (.+?)(?:, -1)?\)\s*$`) + // TODO(aschlosberg) replace regular expressions with a more explicit + // approach for modifying the output code. This likely requires a PR to the + // go-ethereum repo to allow bind.Bind (+/- abigen) to accept an alternate + // template. +) + +// extendGeneratedCode adds ethier-specific functionality to code generated by +// abigen, allowing for interoperability with the ethier/solidity package for +// source-map interpretation at runtime. +func extendGeneratedCode(generated, combinedJSON *bytes.Buffer) ([]byte, error) { + meta := struct { + SourceList []string `json:"sourceList"` + Version string `json:"version"` + + Contracts map[string]*compiler.Contract + CombinedJSON string + }{CombinedJSON: combinedJSON.String()} + + if err := json.Unmarshal(combinedJSON.Bytes(), &meta); err != nil { + return nil, fmt.Errorf("json.Unmarshal([solc output], %T): %v", &meta, err) + } + + cs, err := compiler.ParseCombinedJSON(combinedJSON.Bytes(), "", "", meta.Version, "") + if err != nil { + return nil, fmt.Errorf("compiler.ParseCombinedJSON(): %v", err) + } + meta.Contracts = cs + for k, c := range meta.Contracts { + if c.RuntimeCode == "0x" { + delete(meta.Contracts, k) + } + } + + if err := extraTemplate.Execute(generated, meta); err != nil { + return nil, fmt.Errorf("%T.Execute(): %v", extraTemplate, err) + } + + // When using vm.Config.Trace, the only contract-identifying information is + // the address to which the transaction was sent. We must therefore modify + // every DeployFoo() function to save the address(es) at which the contract + // is deployed. + lines := strings.Split(generated.String(), "\n") + for i, l := range lines { + matches := deployedRegexp.FindStringSubmatch(l) + if len(matches) == 0 { + continue + } + lines[i] = fmt.Sprintf( + `deployedContracts[address] = %q // Added by ethier gen + %s`, + matches[1], l, + ) + } + + // Libraries have their addresses string-replaced directly into contract + // code, which we need to mirror for the runtime code too. + for i, l := range lines { + matches := libReplacementRegexp.FindStringSubmatch(l) + if len(matches) == 0 { + continue + } + lines[i] = fmt.Sprintf( + `%s + RuntimeSourceMaps[%q].RuntimeCode = strings.Replace(RuntimeSourceMaps[%[2]q].RuntimeCode, "__$%s$__", %s, -1)`, + l, matches[1], matches[2], matches[3], + ) + } + + // Effectively the same as running goimports on the (ugly) generated code. + fset := token.NewFileSet() + f, err := parser.ParseFile(fset, "generated.go", strings.Join(lines, "\n"), parser.ParseComments|parser.AllErrors) + if err != nil { + return nil, fmt.Errorf("parser.ParseFile(%T, …): %v", fset, err) + } + for _, pkg := range []string{ + "github.com/ethereum/go-ethereum/common/compiler", + "github.com/divergencetech/ethier/solidity", + } { + if !astutil.AddImport(fset, f, pkg) { + return nil, fmt.Errorf("add import %q to generated Go: %v", pkg, err) + } + } + + buf := bytes.NewBuffer(nil) + if err := format.Node(buf, fset, f); err != nil { + return nil, fmt.Errorf("format.Node(%T, %T, %T): %v", buf, fset, f, err) + } + return buf.Bytes(), nil } diff --git a/ethier/gen_extra.go.tmpl b/ethier/gen_extra.go.tmpl new file mode 100644 index 0000000..b1a23b6 --- /dev/null +++ b/ethier/gen_extra.go.tmpl @@ -0,0 +1,52 @@ +/** + * + * Additional code added by ethier, beyond standard abigen output. + * + */ + +const ( + // SolCVersion is the version of the Solidity compiler used to create this + // file. + SolCVersion = {{quote .Version}} + + // CombinedJSON is the raw combined-JSON output of solc, passed to abigen to + // create this file. + CombinedJSON = {{quote .CombinedJSON}} +) + +var ( + // SourceList is the list of source files used by solc when compiling these + // contracts. Their indices correspond to the file numbers in the source + // maps. + SourceList = {{stringSlice .SourceList}} + + // RuntimeSourceMaps contains, for each compiled contract, the runtime + // binary and its associated source map. With a program counter pointing to + // an instruction in the runtime binary, this is sufficient to determine the + // respective location in the Solidity code from which the binary was + // compiled. + RuntimeSourceMaps = map[string]*compiler.Contract{ + {{- range $src, $c := .Contracts }} + {{quote (contract $src)}}: { + RuntimeCode: {{quote $c.RuntimeCode}}, + Info: compiler.ContractInfo{ + SrcMapRuntime: {{quote $c.Info.SrcMapRuntime}}, + }, + }, + {{- end }} + } +) + +// deployedContracts tracks which contract is deployed at each address. The +// standard abigen Deploy() functions have been modified to set +// the value of this map to before returning the deployment +// address. This allows SourceMap() to function correctly. +var deployedContracts = make(map[common.Address]string) + +// SourceMap returns a new SourceMap, able to convert program counters to +// Solidity source offsets. SourceMap() must be called after contracts are +// deployed otherwise they won't be registered by contract address (only by +// contract name). +func SourceMap() (*solidity.SourceMap, error) { + return solidity.NewSourceMap(SourceList, RuntimeSourceMaps, deployedContracts) +} \ No newline at end of file diff --git a/go.mod b/go.mod index 670f718..8c138f8 100644 --- a/go.mod +++ b/go.mod @@ -3,29 +3,33 @@ module github.com/divergencetech/ethier go 1.17 require ( + github.com/bazelbuild/tools_jvm_autodeps v0.0.0-20180917073602-62694dd50b91 github.com/dustin/go-humanize v1.0.0 - github.com/ethereum/go-ethereum v1.10.12 + github.com/ethereum/go-ethereum v1.10.18 github.com/google/go-cmp v0.5.4 github.com/google/tink/go v1.6.1 github.com/h-fam/errdiff v1.0.2 github.com/miguelmota/go-ethereum-hdwallet v0.1.1 github.com/spf13/cobra v0.0.3 github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef + golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023 ) require ( github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect github.com/VictoriaMetrics/fastcache v1.6.0 // indirect github.com/btcsuite/btcd v0.21.0-beta // indirect + github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce // indirect github.com/cespare/xxhash/v2 v2.1.1 // indirect - github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea // indirect + github.com/deckarep/golang-set v1.8.0 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect github.com/edsrzf/mmap-go v1.0.0 // indirect github.com/go-ole/go-ole v1.2.1 // indirect github.com/go-stack/stack v1.8.0 // indirect github.com/golang/protobuf v1.4.3 // indirect github.com/golang/snappy v0.0.4 // indirect - github.com/google/uuid v1.1.5 // indirect + github.com/google/uuid v1.2.0 // indirect github.com/gorilla/websocket v1.4.2 // indirect github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect github.com/holiman/bloomfilter/v2 v2.0.3 // indirect @@ -41,10 +45,10 @@ require ( github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect github.com/tklauser/go-sysconf v0.3.5 // indirect github.com/tklauser/numcpus v0.2.2 // indirect - golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 // indirect - golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d // indirect - golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912 // indirect - golang.org/x/text v0.3.6 // indirect + golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect + golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f // indirect + golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 // indirect + golang.org/x/text v0.3.7 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d // indirect google.golang.org/grpc v1.31.1 // indirect diff --git a/go.sum b/go.sum index ce0e950..2ee474f 100644 --- a/go.sum +++ b/go.sum @@ -37,6 +37,9 @@ collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc= +github.com/Azure/azure-sdk-for-go/sdk/azcore v0.21.1/go.mod h1:fBF9PQNqB8scdgpZ3ufzaLntG0AG7C1WjPMsiFOmfHM= +github.com/Azure/azure-sdk-for-go/sdk/internal v0.8.3/go.mod h1:KLF4gFr6DcKFZwSuH8w8yEK6DpFl3LP5rhdvAb7Yz5I= +github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.3.0/go.mod h1:tPaiy8S5bQ+S5sOiDlINkp7+Ef339+Nz5L5XO+cnOHo= github.com/Azure/azure-storage-blob-go v0.7.0/go.mod h1:f9YQKtsG1nMisotuTPpO0tjNuEjKRYAcJU8/ydDI++4= github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= @@ -77,6 +80,8 @@ github.com/aws/aws-sdk-go-v2/service/route53 v1.1.1/go.mod h1:rLiOUrPLW/Er5kRcQ7 github.com/aws/aws-sdk-go-v2/service/sso v1.1.1/go.mod h1:SuZJxklHxLAXgLTc1iFXbEWkXs7QRTQpCLGaKIprQW0= github.com/aws/aws-sdk-go-v2/service/sts v1.1.1/go.mod h1:Wi0EBZwiz/K44YliU0EKxqTCJGUfYTWXrrBwkq736bM= github.com/aws/smithy-go v1.1.0/go.mod h1:EzMw8dbp/YJL4A5/sbhGddag+NPT7q084agLbB9LgIw= +github.com/bazelbuild/tools_jvm_autodeps v0.0.0-20180917073602-62694dd50b91 h1:wcw0i+MQc/Yo8RgkS09xSujJnOMCzZXD6LUxhKxGhMg= +github.com/bazelbuild/tools_jvm_autodeps v0.0.0-20180917073602-62694dd50b91/go.mod h1:V5NR740gn0ZNSM6XAl/FGpzEx0RMTOnhiF/otvqHkIY= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= @@ -85,6 +90,9 @@ github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx2 github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.21.0-beta h1:At9hIZdJW0s9E/fAz28nrz6AmcNlSVucCH796ZteX1M= github.com/btcsuite/btcd v0.21.0-beta/go.mod h1:ZSWyehm27aAuS9bvkATT+Xte3hjHZ+MRgMY/8NJ7K94= +github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= +github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts= @@ -101,7 +109,6 @@ github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOC github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -121,8 +128,13 @@ github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea h1:j4317fAZh7X6GqbFowYdYdI0L9bwxL07jyPZIdepyZ0= github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= +github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4= +github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo= +github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= +github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= github.com/deepmap/oapi-codegen v1.6.0/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M= github.com/deepmap/oapi-codegen v1.8.2/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw= @@ -131,9 +143,12 @@ github.com/dgryski/go-bitstream v0.0.0-20180413035011-3522498ce2c8/go.mod h1:VMa github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= +github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko= +github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v1.6.2/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA= -github.com/dop251/goja v0.0.0-20211011172007-d99e4b8cbf48/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= +github.com/dop251/goja v0.0.0-20220405120441-9037c2b61cbf/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= @@ -145,16 +160,18 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/ethereum/go-ethereum v1.10.4/go.mod h1:nEE0TP5MtxGzOMd7egIrbPJMQBnhVU3ELNxhBglIzhg= -github.com/ethereum/go-ethereum v1.10.12 h1:el/KddB3gLEsnNgGQ3SQuZuiZjwnFTYHe5TwUet5Om4= -github.com/ethereum/go-ethereum v1.10.12/go.mod h1:W3yfrFyL9C1pHcwY5hmRHVDaorTiQxhYBkKyu5mEDHw= +github.com/ethereum/go-ethereum v1.10.18 h1:hLEd5M+UD0GJWPaROiYMRgZXl6bi5YwoTJSthsx5CZw= +github.com/ethereum/go-ethereum v1.10.18/go.mod h1:RD3NhcSBjZpj3k+SnQq24wBrmnmie78P5R/P62iNBD8= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/fjl/gencodec v0.0.0-20220412091415-8bb9e558978c/go.mod h1:AzA8Lj6YtixmJWL+wkKoBGsLWy9gFrAzi4g+5bCKwpY= github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61/go.mod h1:Q0X6pkwTILDlzrGEckF6HKjXe48EgsY/l7K7vhY4MW8= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= @@ -184,6 +201,8 @@ github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr6 github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/golang-jwt/jwt/v4 v4.3.0 h1:kHL1vqdqWNfATmA0FNMdmZNMyZI1U6O31X4rlIPoBog= +github.com/golang-jwt/jwt/v4 v4.3.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -244,8 +263,9 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/tink/go v1.6.1 h1:t7JHqO8Ath2w2ig5vjwQYJzhGEZymedQc90lQXUBa4I= github.com/google/tink/go v1.6.1/go.mod h1:IGW53kTgag+st5yPhKKwJ6u2l+SSp5/v9XF7spovjlY= -github.com/google/uuid v1.1.5 h1:kxhtnfFVi+rYdOALN0B3k9UT86zVJKfBimRaciULW4I= github.com/google/uuid v1.1.5/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= +github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -253,6 +273,7 @@ github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB7 github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/graph-gophers/graphql-go v0.0.0-20201113091052-beb923fada29/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= +github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= github.com/h-fam/errdiff v1.0.2 h1:rPsW4ob2fMOIulwTEoZXaaUIuud7XUudw5SLKTZj3Ss= github.com/h-fam/errdiff v1.0.2/go.mod h1:FOzgnHXSEE3rRvmGXgmiqWl+H3lwLywYm9CSXqXrSTg= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -286,8 +307,8 @@ github.com/holiman/uint256 v1.2.0 h1:gpSYcPLWGv4sG43I2mVLiDZCNDh/EpGjSk8tmtxitHM github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huin/goupnp v1.0.1-0.20210310174557-0ca763054c88/go.mod h1:nNs7wvRfN1eKaMknBydLNQU6146XQim8t4h+q90biWo= -github.com/huin/goupnp v1.0.2 h1:RfGLP+h3mvisuWEyybxNq5Eft3NWhHLPeUN72kpKZoI= -github.com/huin/goupnp v1.0.2/go.mod h1:0dxJBVBHqTMjIUMkESDTNgOOx/Mw5wYIfyFmdzSamkM= +github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ= +github.com/huin/goupnp v1.0.3/go.mod h1:ZxNlw5WqJj6wSsRK5+YfflQGXYfccj5VgQsMNixHM7Y= github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= @@ -303,8 +324,9 @@ github.com/influxdata/promql/v2 v2.12.0/go.mod h1:fxOPu+DY0bqCTCECchSRtWfc+0X19y github.com/influxdata/roaring v0.4.13-0.20180809181101-fc520f41fab6/go.mod h1:bSgUQ7q5ZLSO+bKBGqJiCBGAl+9DxyW63zLTujjUlOE= github.com/influxdata/tdigest v0.0.0-20181121200506-bf2b5ad3c0a9/go.mod h1:Js0mqiSBE6Ffsg94weZZ2c+v/ciT8QRHFOap7EKDrR0= github.com/influxdata/usage-client v0.0.0-20160829180054-6d3895376368/go.mod h1:Wbbw6tYNvwa5dlB6304Sd+82Z3f7PmVZHVKU637d4po= -github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458 h1:6OvNmYgJyexcZ3pYbTI9jWx5tHo1Dee/tWbLMfPe2TA= github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= +github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= +github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1CVv03EnqU1wYL2dFwXxW2An0az9JTl/ZsqXQeBlkU= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= @@ -320,7 +342,7 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0= github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= -github.com/karalabe/usb v0.0.0-20211005121534-4c5740d64559/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= +github.com/karalabe/usb v0.0.2/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= @@ -336,6 +358,7 @@ github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v0.0.0-20170224010052-a616ab194758/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= @@ -381,6 +404,7 @@ github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8oh github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= @@ -393,6 +417,7 @@ github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= @@ -485,6 +510,7 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -506,8 +532,9 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -521,6 +548,7 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20220426173459-3bcf042a4bf5/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -543,6 +571,8 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -576,13 +606,16 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d h1:20cMwl2fHAzkJMEA+8J4JgqBQcQGzbisXo31MIeenXI= +golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f h1:OfiFi4JbukWwe3lzw+xunroH1mnC1e2Gy5cxNJApiSY= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -652,8 +685,9 @@ golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912 h1:uCLL3g5wH2xjxVREVuAbP9JM5PPKjRbXKRa6IBjkzmU= -golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 h1:id054HUawV2/6IGm2IV8KZQjqtwAOo2CYlOToYqa0d0= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -664,8 +698,9 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -694,6 +729,7 @@ golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191126055441-b0650ceb63d9/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -718,6 +754,8 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023 h1:0c3L82FDQ5rt1bjTBlchS8t6RQ6299/+5bWMnRLh+uI= +golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -836,8 +874,9 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/solidity/solidity.go b/solidity/solidity.go new file mode 100644 index 0000000..afe64e2 --- /dev/null +++ b/solidity/solidity.go @@ -0,0 +1,203 @@ +// Package solidity provides a source map to determine original code locations +// from op codes in EVM traces. This package doesn't typically need to be used +// directly, and can be automatically supported by adding the source-map flag to +// `ethier gen`. +// +// See https://docs.soliditylang.org/en/v0.8.14/internals/source_mappings.html +// for more information. +package solidity + +import ( + "encoding/hex" + "fmt" + "strconv" + "strings" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/compiler" + "github.com/ethereum/go-ethereum/core/vm" +) + +// NewSourceMap returns a SourceMap based on solc --combined-json output, +// coupled with deployment addresses of the contracts. The sources are the +// sourceList array from the combined JSON, and the order must not be changed. +// The key of the contracts map must match the value of the deployed map to +// allow the SourceMap to find the correct mapping when only an address is +// available. +func NewSourceMap(sources []string, contracts map[string]*compiler.Contract, deployed map[common.Address]string) (*SourceMap, error) { + // See https://docs.soliditylang.org/en/v0.8.14/internals/source_mappings.html + + sm := &SourceMap{ + sources: sources, + contracts: make(map[string]*contractMap), + deployed: deployed, + } + + for name, c := range contracts { + cm := new(contractMap) + if err := cm.parseCode(c); err != nil { + return nil, fmt.Errorf("%q: %v", name, err) + } + if err := cm.parseSrcMap(c); err != nil { + return nil, fmt.Errorf("%q: %v", name, err) + } + sm.contracts[name] = cm + } + + return sm, nil +} + +// A SourceMap maps a program counter from an EVM trace to the original position +// in the Solidity source code. +type SourceMap struct { + sources []string + contracts map[string]*contractMap + deployed map[common.Address]string +} + +// Source returns the code location that was compiled into the instruction at +// the specific program counter in the deployed contract. +func (sm *SourceMap) Source(contract common.Address, pc uint64) (Location, bool) { + name, ok := sm.deployed[contract] + if !ok { + return Location{}, false + } + return sm.SourceByName(name, pc) +} + +// SourceByName functions identically to Source but doesn't require that the +// contract has been deployed. NOTE that there isn't a one-to-one mapping +// between runtime byte code (i.e. program counter) and instruction number +// because the PUSH* instructions require additional bytes; see +// https://docs.soliditylang.org/en/v0.8.14/internals/source_mappings.html. +func (sm *SourceMap) SourceByName(contract string, pc uint64) (Location, bool) { + cm, ok := sm.contracts[contract] + if !ok { + return Location{}, false + } + p, ok := cm.codeLocation(pc) + if !ok { + return Location{}, false + } + + if p.FileIdx < len(sm.sources) { + p.Source = sm.sources[p.FileIdx] + } + return p, true +} + +// A Location is an offset-based location in a Solidity file. Using notation +// described in https://docs.soliditylang.org/en/v0.8.14/internals/source_mappings.html, +// s = Start, l = Length, f = FileIdx, j = Jump, and m = ModifierDepth. +type Location struct { + Start, Length int + + // FileIdx refers to the index of the source file in the inputs to solc, as + // passed to NewSourceMap(), but can generally be ignored in favour of the + // Source, which is determined from the NewSourceMap() input. + FileIdx int + Source string + + Jump JumpType + ModifierDepth int +} + +// A JumpType describes the action of a JUMP instruction. +type JumpType string + +// Possible JumpType values. +const ( + FunctionIn = JumpType(`i`) + FunctionOut = JumpType(`o`) + RegularJump = JumpType(`-`) +) + +// A contractMap is the contract-specific implementation of SourceMap as a +// SourceMap can be used on an arbitrary set of contracts. +type contractMap struct { + locations []Location + pcToInstruction map[uint64]int +} + +func (cm *contractMap) codeLocation(pc uint64) (Location, bool) { + i, ok := cm.pcToInstruction[pc] + if !ok { + return Location{}, false + } + return cm.locations[i], true +} + +// parseCode converts a Contract's runtime byte code into a mapping from +// program counter (position in byte code) to instruction number because the +// PUSH* OpCodes require additional byte code but the source-map is based on +// instruction number. It saves the mapping to the contractMap. +func (cm *contractMap) parseCode(c *compiler.Contract) error { + code, err := hex.DecodeString(strings.TrimPrefix(c.RuntimeCode, "0x")) + if err != nil { + return fmt.Errorf("hex.DecodeString(%T.RuntimeCode): %v", c, err) + } + + var instruction int + pcToInstruction := make(map[uint64]int) + + for i, n := 0, len(code); i < n; i++ { + pcToInstruction[uint64(i)] = instruction + instruction++ + + c := vm.OpCode(code[i]) + if c.IsPush() { + i += int(c - vm.PUSH0) + } + } + cm.pcToInstruction = pcToInstruction + + return nil +} + +// parseSrcMap decompresses the Contract's runtime source map, storing each of +// the locations in the contractMap's `locations` slice. The indices in the +// slice correspond to the values in cm.pcToInstruction. +func (cm *contractMap) parseSrcMap(c *compiler.Contract) error { + instructions := strings.Split(c.Info.SrcMapRuntime, ";") + cm.locations = make([]Location, 0, len(instructions)) + + const nFields = 5 + var last, curr [nFields]string + for _, ins := range instructions { + copy(curr[:], strings.Split(ins, ":")) + for i, n := 0, len(curr); i < nFields; i++ { + if i < n && curr[i] != "" { + last[i] = curr[i] + } else { + curr[i] = last[i] + } + } + + start, err := strconv.Atoi(curr[0]) + if err != nil { + return fmt.Errorf("parse `s`: %v", err) + } + length, err := strconv.Atoi(curr[1]) + if err != nil { + return fmt.Errorf("parse `l`: %v", err) + } + fileIdx, err := strconv.Atoi(curr[2]) + if err != nil { + return fmt.Errorf("parse `f`: %v", err) + } + modDepth, err := strconv.Atoi(curr[4]) + if err != nil { + return fmt.Errorf("parse `m`: %v", err) + } + + cm.locations = append(cm.locations, Location{ + Start: start, + Length: length, + FileIdx: fileIdx, + Jump: JumpType(curr[3]), + ModifierDepth: modDepth, + }) + } + + return nil +} diff --git a/solidity/solidity_test.go b/solidity/solidity_test.go new file mode 100644 index 0000000..86d2068 --- /dev/null +++ b/solidity/solidity_test.go @@ -0,0 +1,44 @@ +package solidity + +import ( + "fmt" + "strings" + "testing" + + "github.com/ethereum/go-ethereum/common/compiler" +) + +func (cm *contractMap) uncompressed() string { + var parts []string + for _, p := range cm.locations { + parts = append(parts, fmt.Sprintf("%d:%d:%d:%s:%d", p.Start, p.Length, p.FileIdx, p.Jump, p.ModifierDepth)) + } + return strings.Join(parts, ";") +} + +func TestMapDecompression(t *testing.T) { + const ( + // From the example at + // https://docs.soliditylang.org/en/v0.8.14/internals/source_mappings.html + // but including modifier depth and jump type. + compressed = `1:2:1:-:0;:9;2:1:2;;` + uncompressed = `1:2:1:-:0;1:9:1:-:0;2:1:2:-:0;2:1:2:-:0;2:1:2:-:0` + ) + + in := map[string]*compiler.Contract{ + "dummy": { + RuntimeCode: "0x00", + Info: compiler.ContractInfo{ + SrcMapRuntime: compressed, + }, + }, + } + sm, err := NewSourceMap(nil, in, nil) + if err != nil { + t.Fatalf("NewSourceMap(%+v): %v", in, err) + } + + if got, want := sm.contracts["dummy"].uncompressed(), uncompressed; got != want { + t.Errorf("NewSourceMap(%+v) got uncompressed mapping %q; want %q", in, got, want) + } +} diff --git a/solidity/source_map_test.go b/solidity/source_map_test.go new file mode 100644 index 0000000..77f16e4 --- /dev/null +++ b/solidity/source_map_test.go @@ -0,0 +1,168 @@ +package solidity_test + +import ( + "fmt" + "math/big" + "testing" + "time" + + "github.com/bazelbuild/tools_jvm_autodeps/thirdparty/golang/parsers/util/offset" + "github.com/divergencetech/ethier/ethtest" + "github.com/divergencetech/ethier/solidity" + "github.com/divergencetech/ethier/solidity/srcmaptest" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/google/go-cmp/cmp" +) + +func TestSourceMap(t *testing.T) { + sim := ethtest.NewSimulatedBackendTB(t, 1) + + // Deploying contracts ensures that we test whether their addresses are + // passed to the SourceMap when it's constructed. + _, _, t0, err := srcmaptest.DeploySourceMapTest0(sim.Acc(0), sim) + if err != nil { + t.Fatalf("DeploySourceMapTest0() error %v", err) + } + _, _, t1, err := srcmaptest.DeploySourceMapTest1(sim.Acc(0), sim) + if err != nil { + t.Fatalf("DeploySourceMapTest1() error %v", err) + } + _, _, t2, err := srcmaptest.DeploySourceMapTest2(sim.Acc(0), sim) + if err != nil { + t.Fatalf("DeploySourceMapTest2() error %v", err) + } + + // As we need to test the SourceMap against actual program counters in the + // VM, we plug it into a vm.EVMLogger that is used to trace the + // SimulatedBackend on all calls to the tX test-contracts above. + src, err := srcmaptest.SourceMap() + if err != nil { + t.Fatalf("srcmaptest.SourceMap() error %v", err) + } + + cfg := sim.Blockchain().GetVMConfig() + cfg.Debug = true + spy := &chainIDInterceptor{ + src: src, + } + cfg.Tracer = spy + + for _, fn := range [](func(*bind.TransactOpts) (*types.Transaction, error)){ + t0.Id, t0.IdPlusOne, t0.FromLib, t1.Id, t2.Id, + } { + sim.Must(t, "")(fn(sim.Acc(0))) + } + + type pos struct { + File string + // Line and Col are 1-indexed as this is how IDEs display them. + Line, Col int + Length int + } + wantLen := len("chainid()") + want := []pos{ + { + File: "solidity/srcmaptest/SourceMapTest.sol", + Line: 25, + Col: 24, + Length: wantLen, + }, + { + File: "solidity/srcmaptest/SourceMapTest.sol", + Line: 32, + Col: 31, + Length: wantLen, + }, + { + File: "solidity/srcmaptest/SourceMapTest.sol", + Line: 14, + Col: 24, + Length: wantLen, + }, + { + File: "solidity/srcmaptest/SourceMapTest.sol", + Line: 49, + Col: 24, + Length: wantLen, + }, + { + File: "solidity/srcmaptest/SourceMapTest2.sol", + Line: 15, + Col: 24, + Length: wantLen, + }, + } + + var got []pos + for _, g := range spy.got { + // TODO: add this functionality to the *solidity.SourceMap itself. + m := offset.NewMapper(string(srcmaptest.ReadSourceFile(t, g.Source))) + line, col, err := m.LineAndColumn(g.Start) + if err != nil { + t.Fatal(err) + } + got = append(got, pos{ + File: g.Source, + Line: line + 1, // 1-indexed like an IDE + Col: col + 1, + Length: g.Length, + }) + } + + if diff := cmp.Diff(want, got); diff != "" { + t.Error(diff) + } +} + +// chainIDInterceptor is a vm.EVMLogger that listens for vm.CHAINID operations, +// recording the solidity.Pos associated with each call. +type chainIDInterceptor struct { + src *solidity.SourceMap + + // contracts are a stack of contract addresses with the last entry of the + // slice being the current contract, against which the pc is compared when + // inspecting the source map. Without a stack (i.e. always using the + // "bottom" contract, to which the tx is initiated) the returned source will + // function incorrectly on library calls. + contracts []common.Address + got []solidity.Location +} + +func (i *chainIDInterceptor) CaptureStart(evm *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { + i.contracts = []common.Address{to} +} + +func (i *chainIDInterceptor) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { + if op != vm.CHAINID { + return + } + + c := i.contracts[len(i.contracts)-1] + if pos, ok := i.src.Source(c, pc); ok { + i.got = append(i.got, pos) + } else { + i.got = append(i.got, solidity.Location{ + Source: fmt.Sprintf("pc %d not found in contract %v", pc, c), + }) + } +} + +func (*chainIDInterceptor) CaptureTxStart(gasLimit uint64) {} + +func (*chainIDInterceptor) CaptureTxEnd(restGas uint64) {} + +func (*chainIDInterceptor) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) {} + +func (i *chainIDInterceptor) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { + i.contracts = append(i.contracts, to) +} + +func (i *chainIDInterceptor) CaptureExit(output []byte, gasUsed uint64, err error) { + i.contracts = i.contracts[:len(i.contracts)-1] +} + +func (*chainIDInterceptor) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) { +} diff --git a/solidity/srcmaptest/SourceMapTest.sol b/solidity/srcmaptest/SourceMapTest.sol new file mode 100644 index 0000000..8f571e0 --- /dev/null +++ b/solidity/srcmaptest/SourceMapTest.sol @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2022 the ethier authors (github.com/divergencetech/ethier) +pragma solidity >=0.8.0 <0.9.0; + +/** +These contracts and libraries result in the CHAINID OpCode in various places, +which we use to test the Go SourceMap implementation because it's an otherwise +obscure OpCode that won't result in false positives. + */ + +library SourceMapTestLib { + function id() external view returns (uint256 chainId) { + assembly { + chainId := chainid() + } + } +} + +contract SourceMapTest0 { + /// @dev Allows functions to be non-view. + event Noop(); + + function id() external returns (uint256 chainId) { + assembly { + chainId := chainid() + } + emit Noop(); + } + + function idPlusOne() external returns (uint256 chainIdPlusOne) { + assembly { + chainIdPlusOne := chainid() + } + chainIdPlusOne++; + emit Noop(); + } + + function fromLib() external returns (uint256) { + emit Noop(); + return SourceMapTestLib.id(); + } +} + +contract SourceMapTest1 { + event Noop(); + + function id() external returns (uint256 chainId) { + assembly { + chainId := chainid() + } + emit Noop(); + } +} diff --git a/solidity/srcmaptest/SourceMapTest2.sol b/solidity/srcmaptest/SourceMapTest2.sol new file mode 100644 index 0000000..1ebea54 --- /dev/null +++ b/solidity/srcmaptest/SourceMapTest2.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2022 the ethier authors (github.com/divergencetech/ethier) +pragma solidity >=0.8.0 <0.9.0; + +/** +See SourceMapTest.sol for rationale. + */ + +contract SourceMapTest2 { + /// @dev Allows functions to be non-view. + event Noop(); + + function id() external returns (uint256 chainId) { + assembly { + chainId := chainid() + } + emit Noop(); + } +} diff --git a/solidity/srcmaptest/srcmaptest.go b/solidity/srcmaptest/srcmaptest.go new file mode 100644 index 0000000..bc72355 --- /dev/null +++ b/solidity/srcmaptest/srcmaptest.go @@ -0,0 +1,31 @@ +// Package srcmaptest is a test-only package of generated Solidity bindings used +// to test the ethier/solidity package. +package srcmaptest + +import ( + "embed" + "strings" + "testing" +) + +//go:generate ethier gen --experimental_src_map SourceMapTest.sol SourceMapTest2.sol + +//go:embed *.sol +var sourceFiles embed.FS + +// ReadSourceFile returns the Solidity source of the specified file. +func ReadSourceFile(t *testing.T, file string) []byte { + t.Helper() + + const prefix = "solidity/srcmaptest/" + + if !strings.HasPrefix(file, prefix) { + t.Fatalf("srcmaptest.ReadSourceFile(%q) file must have prefix %q", file, prefix) + } + + buf, err := sourceFiles.ReadFile(strings.TrimPrefix(file, prefix)) + if err != nil { + t.Fatalf("srcmaptest.%T.ReadFile(%q with prefix trimmed) error %v", sourceFiles, file, err) + } + return buf +}