├── scripts ├── entrypoint.sh ├── stop.sh └── start.sh ├── ethlogspy.png ├── avatar-default.png ├── releases.json ├── configs └── config.yml ├── .gitignore ├── go.mod ├── Dockerfile ├── docker-compose.yml ├── dappnode_package.json ├── LICENSE ├── config.go ├── db.go ├── structs.go ├── logsync.go ├── main.go ├── utils.go ├── README.md └── go.sum /scripts/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ./ethlogspy 4 | -------------------------------------------------------------------------------- /ethlogspy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PaoloRollo/ethlogspy/HEAD/ethlogspy.png -------------------------------------------------------------------------------- /scripts/stop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd /usr/local/ethlogspy && docker-compose stop -------------------------------------------------------------------------------- /avatar-default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PaoloRollo/ethlogspy/HEAD/avatar-default.png -------------------------------------------------------------------------------- /scripts/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd /usr/local/ethlogspy 4 | docker-compose build 5 | nohup docker-compose up & -------------------------------------------------------------------------------- /releases.json: -------------------------------------------------------------------------------- 1 | { 2 | "0.1.0": { 3 | "hash": "/ipfs/QmdgDp1Rr4dU2QaKcoFxjX1aNoH7mHYv2JiXc4AkFceHHX", 4 | "uploadedTo": { 5 | "dappnode": "Thu, 25 Mar 2021 11:21:52 GMT" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /configs/config.yml: -------------------------------------------------------------------------------- 1 | mongo: 2 | connection: "mongodb://mongo:27017" # mongodb connection string 3 | db_name: "ethlogspy" # mongodb database name 4 | redis: 5 | host: "redis" # redis host 6 | port: 6379 # redis port 7 | password: "" # redis password 8 | db: 0 # redis db -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | # DAppNodeSDK release directories 17 | build_* 18 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/PaoloRollo/ethlogspy 2 | 3 | go 1.15 4 | 5 | require ( 6 | github.com/AdhityaRamadhanus/fasthttpcors v0.0.0-20170121111917-d4c07198763a 7 | github.com/ethereum/go-ethereum v1.9.24 8 | github.com/fasthttp/websocket v1.4.1 9 | github.com/go-redis/redis/v8 v8.4.0 10 | github.com/sirupsen/logrus v1.4.2 11 | github.com/valyala/fasthttp v1.17.0 12 | github.com/yeqown/fasthttp-reverse-proxy v0.0.0-20200930023507-ed73ac32bc64 13 | go.mongodb.org/mongo-driver v1.4.3 14 | gopkg.in/yaml.v2 v2.3.0 15 | ) 16 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:latest as build 2 | RUN mkdir -p /usr/local/ethlogspy 3 | COPY *.go /usr/local/ethlogspy/ 4 | COPY configs/config.yml /usr/local/ethlogspy/ 5 | COPY go.mod /usr/local/ethlogspy/ 6 | COPY go.sum /usr/local/ethlogspy/ 7 | COPY scripts/entrypoint.sh /usr/local/ethlogspy/ 8 | WORKDIR /usr/local/ethlogspy 9 | RUN go mod tidy && go build -o ethlogspy *.go 10 | 11 | FROM golang:latest 12 | RUN mkdir -p /usr/local/ethlogspy 13 | WORKDIR /usr/local/ethlogspy 14 | COPY --from=build /usr/local/ethlogspy/ethlogspy . 15 | COPY --from=build /usr/local/ethlogspy/config.yml . 16 | COPY --from=build /usr/local/ethlogspy/entrypoint.sh . 17 | RUN chmod +x entrypoint.sh 18 | EXPOSE 8080 19 | ENTRYPOINT [ "./entrypoint.sh" ] -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | mongo: 4 | image: "mongo:latest" 5 | container_name: mongo 6 | ports: 7 | - "27017:27017" 8 | redis: 9 | image: "redis:latest" 10 | container_name: redis 11 | ports: 12 | - "6379:6379" 13 | ethlogspy: 14 | build: . 15 | container_name: ethlogspy 16 | environment: 17 | - NODE_HOST=geth.dappnode 18 | - NODE_PORT=8546 19 | - CORS_ORIGIN=* 20 | - BLOCK_NUMBER=0 21 | ports: 22 | - "8080:8080" 23 | depends_on: 24 | - mongo 25 | - redis 26 | links: 27 | - mongo 28 | - redis 29 | restart: unless-stopped 30 | image: "ethlogspy.ethlogspy.public.dappnode.eth:0.1.0" 31 | -------------------------------------------------------------------------------- /dappnode_package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ethlogspy.public.dappnode.eth", 3 | "version": "0.1.0", 4 | "shortDescription": "Reverse proxy for Ethereum nodes that stores logs information for a faster retrieval.", 5 | "description": "Reverse proxy for Ethereum nodes that stores logs information for a faster retrieval; in more words, just sends back to the given node except for the `eth_getLogs` function, where it retrieves the data from the database (MongoDB) or from the cache (Redis) and returns the data in the same format that the real node would use, basically replicating its response. It is fully compatible with any `web3` library (such as `web3js`), so your dApp can easily retrieve the logs just by using its instance. The environment variables are the following: `NODE_HOST` corresponds to the Ethereum node host name (eg. `geth.dappnode`); `NODE_PORT` is the `websocket` port of that node (eg. 8546); `CORS_ORIGIN` is used to restrict the requests to only allowed origins (default is `*`); `BLOCK_NUMBER` is the block where the log sync must start.", 6 | "type": "service", 7 | "author": "orbulo.eth (https://github.com/PaoloRollo)", 8 | "architectures": ["linux/amd64", "linux/arm64"], 9 | "categories": ["Developer tools"], 10 | "links": { 11 | "endpoint": "http://ethlogspy.public.dappnode:8080", 12 | "homepage": "https://github.com/PaoloRollo/ethlogspy" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/PaoloRollo/ethlogspy" 17 | }, 18 | "license": "MIT" 19 | } 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Paolo Rollo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | ==================================================== 24 | 25 | Hope you enjoy your new logo, here are the people that 26 | made your beautiful logo happen :) 27 | font name: ArchivoNarrow-Bold 28 | font link: https://fonts.google.com/specimen/Archivo+Narrow 29 | font author: Omnibus-Type 30 | font author site: http://www.omnibus-type.com/ 31 | 32 | 33 | icon designer: Dan Hetteix 34 | icon designer link: /DHETTEIX 35 | 36 | {"bg":"transparent","icon":"#488eff","font":"#2e3034","slogan":"#424448"} 37 | -------------------------------------------------------------------------------- /config.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "os" 6 | "path" 7 | "strconv" 8 | 9 | "gopkg.in/yaml.v2" 10 | ) 11 | 12 | // Configuration retains the config struct for the EthLogSpy instance 13 | var Configuration *Config 14 | 15 | // GetConfig gets a filesystem path as an argument and returns, if successful, 16 | // a Config object that can be used throughout the application 17 | func GetConfig(configPath string, vars ...string) { 18 | file, err := os.Open(path.Join(configPath, "config.yml")) 19 | if err != nil { 20 | // Return an error if it's given 21 | log.Fatalf("error opening file: %v", err) 22 | } 23 | // Decode the yaml file into the struct 24 | yamlDecoder := yaml.NewDecoder(file) 25 | if err := yamlDecoder.Decode(&Configuration); err != nil { 26 | log.Fatalf("error decoding file: %v", err) 27 | } 28 | var nodeHost, corsOrigin string 29 | nodeHost = os.Getenv("NODE_HOST") 30 | if len(nodeHost) == 0 { 31 | nodeHost = "localhost" 32 | } 33 | corsOrigin = os.Getenv("CORS_ORIGIN") 34 | if len(corsOrigin) == 0 { 35 | corsOrigin = "*" 36 | } 37 | Configuration.Node.Host = nodeHost 38 | Configuration.Server.CorsOrigin = corsOrigin 39 | var nodePortInt, blockNumberInt int 40 | nodePort := os.Getenv("NODE_PORT") 41 | if len(nodePort) == 0 { 42 | nodePortInt = 8545 43 | } else { 44 | nodePortInt, err = strconv.Atoi(nodePort) 45 | if err != nil { 46 | nodePortInt = 8545 47 | } 48 | } 49 | Configuration.Node.Port = nodePortInt 50 | blockNumber := os.Getenv("BLOCK_NUMBER") 51 | if len(blockNumber) == 0 { 52 | blockNumberInt = 0 53 | } else { 54 | blockNumberInt, err = strconv.Atoi(blockNumber) 55 | if err != nil { 56 | blockNumberInt = 8545 57 | } 58 | } 59 | Configuration.Server.FromBlock = uint64(blockNumberInt) 60 | } 61 | -------------------------------------------------------------------------------- /db.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | 7 | "github.com/ethereum/go-ethereum/common/hexutil" 8 | "github.com/ethereum/go-ethereum/core/types" 9 | ) 10 | 11 | func storeLogs(logs []types.Log) { 12 | // Iterate on all the logs found to create the mongo log 13 | for _, log := range logs { 14 | logJSON, err := log.MarshalJSON() 15 | if err != nil { 16 | logger.Errorf("error while marshaling log: %v", err) 17 | continue 18 | } 19 | logJSONString := string(logJSON) 20 | logger.Infof("currenty iterating: %s", logJSONString) 21 | topics := []string{} 22 | for _, topic := range log.Topics { 23 | topics = append(topics, topic.String()) 24 | } 25 | JSONlog := JSONLog{} 26 | mongoLog := Log{} 27 | err = json.Unmarshal(logJSON, &JSONlog) 28 | if err != nil { 29 | logger.Errorf("error while unmarshaling log: %v", err) 30 | continue 31 | } 32 | mongoLog.Address = JSONlog.Address 33 | mongoLog.Removed = JSONlog.Removed 34 | mongoLog.BlockHash = JSONlog.BlockHash 35 | mongoLog.Data = JSONlog.Data 36 | mongoLog.Topics = JSONlog.Topics 37 | logIndex, err := hexutil.DecodeBig(JSONlog.LogIndex) 38 | if err != nil { 39 | logger.Errorf("error while decoding log index: %v", err) 40 | continue 41 | } 42 | mongoLog.LogIndex = int(logIndex.Int64()) 43 | transactionIndex, err := hexutil.DecodeBig(JSONlog.TransactionIndex) 44 | if err != nil { 45 | logger.Errorf("error while decoding transaction index: %v", err) 46 | continue 47 | } 48 | mongoLog.TransactionIndex = int(transactionIndex.Int64()) 49 | blockNumber, err := hexutil.DecodeUint64(JSONlog.BlockNumber) 50 | if err != nil { 51 | logger.Errorf("error while decoding block number: %v", err) 52 | continue 53 | } 54 | mongoLog.BlockNumber = int(blockNumber) 55 | collection := mongoDatabase.Collection("logs") 56 | logger.Infof("inserting log in the collection: %v", mongoLog) 57 | _, err = collection.InsertOne(context.Background(), mongoLog) 58 | if err != nil { 59 | logger.Errorf("error while inserting log: %v", err) 60 | continue 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /structs.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // Config struct is used to define the application configuration object 4 | type Config struct { 5 | Mongo struct { 6 | Connection string `yaml:"connection"` 7 | DbName string `yaml:"db_name"` 8 | } 9 | Node struct { 10 | Host string `yaml:"host"` 11 | Port int `yaml:"port"` 12 | } 13 | Redis struct { 14 | Host string `yaml:"host"` 15 | Port int `yaml:"port"` 16 | Password string `yaml:"password"` 17 | DB int `yaml:"db"` 18 | } 19 | Server struct { 20 | FromBlock uint64 `yaml:"from_block"` 21 | CorsOrigin string `yaml:"cors_origin"` 22 | } 23 | } 24 | 25 | // JSONLog Struct 26 | type JSONLog struct { 27 | Removed bool `json:"removed"` 28 | LogIndex string `json:"logIndex"` 29 | TransactionIndex string `json:"transactionIndex"` 30 | BlockNumber string `json:"blockNumber"` 31 | BlockHash string `json:"blockHash"` 32 | Address string `json:"address"` 33 | Data string `json:"data"` 34 | Topics []string `json:"topics"` 35 | } 36 | 37 | // LogRequest struct 38 | type LogRequest struct { 39 | JSONRPC string `json:"jsonrpc"` 40 | Method string `json:"method"` 41 | Params []LogRequestFilter `json:"params"` 42 | ID int `json:"id"` 43 | } 44 | 45 | // LogLatestBlockRead struct 46 | type LogLatestBlockRead struct { 47 | Address string `json:"address" bson:"address,omitempty"` 48 | BlockNumber uint64 `json:"blockNumber" bson:"blockNumber,omitempty"` 49 | } 50 | 51 | // ServerLatestBlockRead struct 52 | type ServerLatestBlockRead struct { 53 | ID int `json:"id" bson:"_id"` 54 | BlockNumber uint64 `json:"blockNumber" bson:"blockNumber,omitempty"` 55 | } 56 | 57 | // LogRequestFilter struct 58 | type LogRequestFilter struct { 59 | FromBlock interface{} `json:"fromBlock"` 60 | ToBlock interface{} `json:"toBlock"` 61 | Address string `json:"address"` 62 | Topics []string `json:"topics"` 63 | } 64 | 65 | // Log Struct 66 | type Log struct { 67 | Removed bool `json:"removed" bson:"removed,omitempty"` 68 | LogIndex int `json:"logIndex" bson:"logIndex,omitempty"` 69 | TransactionIndex int `json:"transactionIndex" bson:"transactionIndex,omitempty"` 70 | BlockNumber int `json:"blockNumber" bson:"blockNumber,omitempty"` 71 | BlockHash string `json:"blockHash" bson:"blockHash,omitempty"` 72 | Address string `json:"address" bson:"address,omitempty"` 73 | Data string `json:"data" bson:"data,omitempty"` 74 | Topics []string `json:"topics" bson:"topics,omitempty"` 75 | } 76 | 77 | // LogResponse struct 78 | type LogResponse struct { 79 | ID int `json:"id"` 80 | JSONRPC string `json:"jsonrpc"` 81 | Result []Log `json:"result"` 82 | } 83 | -------------------------------------------------------------------------------- /logsync.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "math/big" 6 | 7 | "github.com/ethereum/go-ethereum" 8 | "github.com/ethereum/go-ethereum/core/types" 9 | "go.mongodb.org/mongo-driver/bson" 10 | ) 11 | 12 | func subscribeToHead() { 13 | logger.Info("starting blockchain head subscription..") 14 | headers := make(chan *types.Header) 15 | sub, err := ethClient.SubscribeNewHead(context.Background(), headers) 16 | if err != nil { 17 | logger.Fatalf("error while subscribing to blockchain head: %v", err) 18 | } 19 | for { 20 | select { 21 | case err := <-sub.Err(): 22 | logger.Errorf("error during blockchain head subscription: %v", err) 23 | case header := <-headers: 24 | logger.Infof("new block received, hash: %s", header.Hash().String()) 25 | block, err := ethClient.BlockByHash(context.Background(), header.Hash()) 26 | if err != nil { 27 | logger.Errorf("error while retrieving block by hash %s: %v", header.Hash(), err) 28 | continue 29 | } 30 | logs, err := ethClient.FilterLogs(context.Background(), ethereum.FilterQuery{ 31 | FromBlock: block.Number(), 32 | ToBlock: block.Number(), 33 | }) 34 | if err != nil { 35 | logger.Errorf("error while retrieving logs by block number %d: %v", block.Number().Int64(), err) 36 | continue 37 | } 38 | // Iterate on all the logs found to create the mongo log 39 | storeLogs(logs) 40 | } 41 | } 42 | } 43 | 44 | func syncLogs() { 45 | collection := mongoDatabase.Collection("logLatestBlockRead") 46 | blockNumber, err := ethClient.BlockNumber(context.TODO()) 47 | if err != nil { 48 | logger.Errorf("error while retrieving block number: %v", err) 49 | return 50 | } 51 | var serverLatestBlockRead ServerLatestBlockRead 52 | // Check latest block read 53 | res := collection.FindOne(context.Background(), bson.M{"_id": 0}) 54 | if res.Err() != nil { 55 | err := res.Decode(&serverLatestBlockRead) 56 | if err != nil { 57 | serverLatestBlockRead = ServerLatestBlockRead{BlockNumber: Configuration.Server.FromBlock} 58 | } 59 | } 60 | // Build the filter query 61 | query := ethereum.FilterQuery{ 62 | FromBlock: big.NewInt(int64(Configuration.Server.FromBlock)), 63 | ToBlock: big.NewInt(int64(blockNumber)), 64 | } 65 | logger.Infof("retrieving logs using the following query: %+v", query) 66 | // Retrieve the logs 67 | logs, err := ethClient.FilterLogs(context.Background(), query) 68 | if err != nil { 69 | logger.Errorf("error while filtering logs: %v", err) 70 | return 71 | } 72 | logger.Info("logs retrieved successfully from the node, storing them..") 73 | // Iterate on all the logs found to create the mongo log 74 | storeLogs(logs) 75 | serverLatestBlockRead.BlockNumber = blockNumber 76 | res = collection.FindOneAndUpdate(context.Background(), bson.M{"_id": 0}, bson.M{"$set": serverLatestBlockRead}) 77 | if res.Err() != nil { 78 | serverLatestBlockRead.ID = 0 79 | _, err = collection.InsertOne(context.Background(), serverLatestBlockRead) 80 | if err != nil { 81 | logger.Errorf("error while inserting log latest block read: %v", res.Err()) 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "flag" 7 | "fmt" 8 | "strings" 9 | "time" 10 | 11 | cors "github.com/AdhityaRamadhanus/fasthttpcors" 12 | "github.com/ethereum/go-ethereum/ethclient" 13 | "github.com/fasthttp/websocket" 14 | "github.com/go-redis/redis/v8" 15 | "github.com/valyala/fasthttp" 16 | proxy "github.com/yeqown/fasthttp-reverse-proxy" 17 | "go.mongodb.org/mongo-driver/bson" 18 | "go.mongodb.org/mongo-driver/mongo" 19 | "go.mongodb.org/mongo-driver/mongo/options" 20 | ) 21 | 22 | var ( 23 | ethClient *ethclient.Client 24 | proxyServer *proxy.ReverseProxy 25 | wsProxyServer *proxy.WSReverseProxy 26 | rdb *redis.Client 27 | mongoClient *mongo.Client 28 | mongoDatabase *mongo.Database 29 | redisCtx = context.Background() 30 | strContentType = []byte("Content-Type") 31 | strApplicationJSON = []byte("application/json") 32 | upgrader = websocket.FastHTTPUpgrader{ 33 | WriteBufferSize: 1024, 34 | ReadBufferSize: 1024, 35 | } 36 | ) 37 | 38 | // ProxyHandler is the main core 39 | func ProxyHandler(ctx *fasthttp.RequestCtx) { 40 | if string(ctx.Method()) == "POST" { 41 | var req LogRequest 42 | json.Unmarshal(ctx.PostBody(), &req) 43 | logger.Infof("new request incoming for method: %s", req.Method) 44 | if strings.ToLower(req.Method) == "eth_getlogs" { 45 | logger.Infof("spying on eth_getLogs.. shh...") 46 | err := getLogs(req, ctx) 47 | if err != nil { 48 | logger.Errorf("error while retrieving logs: %v", err) 49 | } else { 50 | return 51 | } 52 | } 53 | } 54 | // Serve the context 55 | proxyServer.ServeHTTP(ctx) 56 | } 57 | 58 | // WebsocketProxyHandler is the websocket main core 59 | func WebsocketProxyHandler(ctx *fasthttp.RequestCtx) { 60 | err := upgrader.Upgrade(ctx, func(ws *websocket.Conn) { 61 | // Defer connection close 62 | defer ws.Close() 63 | for { 64 | var req LogRequest 65 | _, message, err := ws.ReadMessage() 66 | if err != nil { 67 | logger.Errorf("error while reading message: %v", err) 68 | break 69 | } 70 | err = json.Unmarshal(message, &req) 71 | if err != nil { 72 | logger.Errorf("error while marshaling ws body: %v", err) 73 | break 74 | } 75 | if strings.ToLower(req.Method) == "eth_getlogs" { 76 | logResponse, err := getWsLogs(req) 77 | if err != nil { 78 | logger.Errorf("error retrieving logs: %v", err) 79 | break 80 | } 81 | ws.WriteJSON(logResponse) 82 | } else { 83 | // Serve all the requests with the proxy 84 | wsProxyServer.ServeHTTP(ctx) 85 | } 86 | } 87 | }) 88 | if err != nil { 89 | logger.Errorf("error during request context upgrade: %v", err) 90 | return 91 | } 92 | } 93 | 94 | func main() { 95 | var err error 96 | // Setup the logger 97 | setuplogger() 98 | // Parse the config path or use the default value 99 | configPath := flag.String("config", "/usr/local/ethlogspy/", "path to config file") 100 | // Parse the intel mode flag 101 | // intelMode := flag.Bool("intel", false, "activate intel mode") 102 | flag.Parse() 103 | // Validates the path 104 | validatedPath, err := validatePath(*configPath) 105 | // If the path is invalid log the error and exit 106 | if err != nil { 107 | logger.Fatalf("failed to validate path: %v", err) 108 | } 109 | logger.Info("retrieving configuration..") 110 | // Retrieve the config from the config path 111 | GetConfig(*validatedPath) 112 | logger.Info("configuration retrieved successfully..") 113 | // Initializing ETH client 114 | logger.Info("initializing eth client..") 115 | ethClient, err = ethclient.Dial(fmt.Sprintf("ws://%s:%d", Configuration.Node.Host, Configuration.Node.Port)) 116 | if err != nil { 117 | panic(err) 118 | } 119 | logger.Info("eth client initialized successfully..") 120 | // Create proxy server 121 | logger.Info("creating http and ws proxy servers..") 122 | wsProxyServer = proxy.NewWSReverseProxy(fmt.Sprintf("%s:%d", Configuration.Node.Host, Configuration.Node.Port), "/ws") 123 | proxyServer = proxy.NewReverseProxy(fmt.Sprintf("%s:%d", Configuration.Node.Host, Configuration.Node.Port)) 124 | logger.Info("proxy servers created successfully..") 125 | // Connect to redis 126 | logger.Info("connecting to redis..") 127 | rdb = redis.NewClient(&redis.Options{ 128 | Addr: fmt.Sprintf("%s:%d", Configuration.Redis.Host, Configuration.Redis.Port), 129 | Password: Configuration.Redis.Password, 130 | DB: Configuration.Redis.DB, 131 | }) 132 | logger.Info("connected successfully to redis..") 133 | // Create mongo client context with timeout 134 | ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 135 | defer cancel() 136 | // Create mongo client 137 | logger.Info("connecting to mongodb..") 138 | mongoClient, err = mongo.Connect(ctx, options.Client().ApplyURI(Configuration.Mongo.Connection)) 139 | if err != nil { 140 | panic(err) 141 | } 142 | // Call defer mongo client connection 143 | defer func() { 144 | if err = mongoClient.Disconnect(ctx); err != nil { 145 | panic(err) 146 | } 147 | }() 148 | logger.Info("connected successfully to mongodb..") 149 | // Retrieve the database 150 | mongoDatabase = mongoClient.Database(Configuration.Mongo.DbName) 151 | // Check if logs have been already saved 152 | res := mongoDatabase.Collection("logs").FindOne(context.Background(), bson.M{}) 153 | if res.Err() != nil { 154 | start := time.Now() 155 | logger.Info("syncing mongodb with ethereum logs..") 156 | syncLogs() 157 | elapsed := time.Since(start) 158 | logger.Info("logs sync successful, elapsed: ", elapsed) 159 | } 160 | // Start subscribing to logs 161 | go subscribeToHead() 162 | // Create handler 163 | requestHandler := func(ctx *fasthttp.RequestCtx) { 164 | if strings.Contains(string(ctx.Path()), "/ws") { 165 | WebsocketProxyHandler(ctx) 166 | } else { 167 | ProxyHandler(ctx) 168 | } 169 | } 170 | // Add cors middleware 171 | withCors := cors.NewCorsHandler(cors.Options{ 172 | AllowedOrigins: []string{Configuration.Server.CorsOrigin}, 173 | }) 174 | logger.Info("starting ethlogspy server..") 175 | if err := fasthttp.ListenAndServe(":8080", withCors.CorsMiddleware(requestHandler)); err != nil { 176 | logger.Fatal(err) 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /utils.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "os" 8 | "time" 9 | 10 | "github.com/go-redis/redis/v8" 11 | "github.com/sirupsen/logrus" 12 | "github.com/valyala/fasthttp" 13 | "go.mongodb.org/mongo-driver/bson" 14 | ) 15 | 16 | var logger *logrus.Logger 17 | 18 | func setuplogger() { 19 | logger = &logrus.Logger{ 20 | Out: os.Stdout, 21 | Formatter: &logrus.TextFormatter{DisableColors: false, FullTimestamp: true}, 22 | Level: logrus.InfoLevel, 23 | } 24 | } 25 | 26 | func validatePath(path string) (*string, error) { 27 | stat, err := os.Stat(path) 28 | if err != nil { 29 | return nil, err 30 | } 31 | if !stat.IsDir() { 32 | return nil, fmt.Errorf("'%s' is not a valid directory", path) 33 | } 34 | return &path, nil 35 | } 36 | 37 | func getFilter(params []LogRequestFilter) (bson.M, error) { 38 | var bsonFilter bson.M 39 | if len(params) > 0 { 40 | filter := params[0] 41 | logger.Infof("using filter %+v", filter) 42 | if filter.Address != "" { 43 | bsonFilter["address"] = filter.Address 44 | } 45 | if filter.FromBlock != "" { 46 | switch filter.FromBlock.(type) { 47 | case string: 48 | if filter.FromBlock == "earliest" { 49 | bsonFilter["fromBlock"] = bson.M{"$ge": 0} 50 | } else if filter.FromBlock == "latest" { 51 | blockNumber, err := ethClient.BlockNumber(context.TODO()) 52 | if err != nil { 53 | return bsonFilter, err 54 | } 55 | bsonFilter["fromBlock"] = bson.M{"$ge": blockNumber} 56 | } 57 | default: 58 | bsonFilter["fromBlock"] = bson.M{"$ge": filter.FromBlock} 59 | } 60 | } 61 | if filter.ToBlock != "" { 62 | switch filter.ToBlock.(type) { 63 | case string: 64 | if filter.ToBlock == "earliest" { 65 | bsonFilter["toBlock"] = bson.M{"$le": 0} 66 | } else if filter.ToBlock == "latest" { 67 | blockNumber, err := ethClient.BlockNumber(context.TODO()) 68 | if err != nil { 69 | logger.Errorf("error while retrieving block number: %v", err) 70 | return bsonFilter, err 71 | } 72 | bsonFilter["toBlock"] = bson.M{"$le": blockNumber} 73 | } 74 | default: 75 | if filter.ToBlock != nil { 76 | bsonFilter["toBlock"] = bson.M{"$le": filter.ToBlock} 77 | } 78 | } 79 | } 80 | if filter.Topics != nil && len(filter.Topics) > 0 { 81 | bsonFilter["topics"] = bson.M{"$in": filter.Topics} 82 | } 83 | } 84 | return bsonFilter, nil 85 | } 86 | 87 | func getLogs(req LogRequest, ctx *fasthttp.RequestCtx) error { 88 | var logs []Log 89 | // Build the redis key 90 | redisKeyBytes, err := json.Marshal(req) 91 | if err != nil { 92 | return err 93 | } 94 | redisKey := string(redisKeyBytes) 95 | // Retrieve the value from redis, if it exists 96 | val, err := rdb.Get(redisCtx, redisKey).Result() 97 | if err == nil || err == redis.Nil { 98 | // An error has occurred or the key was not set, retrieve it from couchdb 99 | bsonFilter, err := getFilter(req.Params) 100 | if err != nil { 101 | return err 102 | } 103 | // Create find query context 104 | findCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 105 | defer cancel() 106 | // Get logs mongodb collection 107 | collection := mongoDatabase.Collection("logs") 108 | res, err := collection.Find(findCtx, bsonFilter) 109 | if err != nil { 110 | logger.Errorf("error while finding logs on database: %v", err) 111 | return err 112 | } 113 | defer res.Close(findCtx) 114 | for res.Next(findCtx) { 115 | var log Log 116 | if err = res.Decode(&log); err != nil { 117 | logger.Errorf("error while retrieving logs: %v", err) 118 | return err 119 | } 120 | logs = append(logs, log) 121 | } 122 | // Marshal the logs for redis 123 | marshaledLogs, err := json.Marshal(logs) 124 | if err != nil { 125 | logger.Errorf("error while marshaling logs: %v", err) 126 | return err 127 | } 128 | // Set the value in cache 129 | rdb.SetEX(redisCtx, redisKey, string(marshaledLogs), 30*time.Second) 130 | // Encode the value read from mongo 131 | if err := json.NewEncoder(ctx).Encode(LogResponse{ID: req.ID, JSONRPC: req.JSONRPC, Result: logs}); err != nil { 132 | return err 133 | } 134 | return err 135 | } 136 | json.Unmarshal([]byte(val), &logs) 137 | // Encode the value read from the cached val 138 | if err := json.NewEncoder(ctx).Encode(LogResponse{ID: req.ID, JSONRPC: req.JSONRPC, Result: logs}); err != nil { 139 | return err 140 | } 141 | return nil 142 | } 143 | 144 | func getWsLogs(req LogRequest) (LogResponse, error) { 145 | var logs []Log 146 | // Build the redis key 147 | redisKeyBytes, err := json.Marshal(req) 148 | if err != nil { 149 | return LogResponse{}, err 150 | } 151 | redisKey := string(redisKeyBytes) 152 | // Retrieve the value from redis, if it exists 153 | val, err := rdb.Get(redisCtx, redisKey).Result() 154 | if err == nil || err == redis.Nil { 155 | // An error has occurred or the key was not set, retrieve it from couchdb 156 | bsonFilter, err := getFilter(req.Params) 157 | if err != nil { 158 | return LogResponse{}, err 159 | } 160 | // Create find query context 161 | findCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 162 | defer cancel() 163 | // Get logs mongodb collection 164 | collection := mongoDatabase.Collection("logs") 165 | res, err := collection.Find(findCtx, bsonFilter) 166 | if err != nil { 167 | logger.Errorf("error while finding logs on database: %v", err) 168 | return LogResponse{}, err 169 | } 170 | defer res.Close(findCtx) 171 | for res.Next(findCtx) { 172 | var log Log 173 | if err = res.Decode(&log); err != nil { 174 | logger.Errorf("error while retrieving logs: %v", err) 175 | return LogResponse{}, err 176 | } 177 | logs = append(logs, log) 178 | } 179 | // Marshal the logs for redis 180 | marshaledLogs, err := json.Marshal(logs) 181 | if err != nil { 182 | logger.Errorf("error while marshaling logs: %v", err) 183 | return LogResponse{}, err 184 | } 185 | // Set the value in cache 186 | rdb.SetEX(redisCtx, redisKey, string(marshaledLogs), 30*time.Second) 187 | // Return the log response 188 | return LogResponse{ID: req.ID, JSONRPC: req.JSONRPC, Result: logs}, err 189 | } 190 | json.Unmarshal([]byte(val), &logs) 191 | return LogResponse{ID: req.ID, JSONRPC: req.JSONRPC, Result: logs}, nil 192 | } 193 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |
6 |

7 | Reverse proxy for Ethereum nodes that stores logs information for a faster retrieval. 8 |

9 |

10 | version 1.0.0-beta 11 |

12 |
13 |

14 | Status · 15 | Description · 16 | Features · 17 | Install · 18 | DAppNode · 19 | Example · 20 | Security · 21 | Contributing 22 |

23 |
24 | 25 | --- 26 | 27 | ## Status 28 | 29 | **EthLogSpy** is currently in **beta** version. 30 | 31 | --- 32 | 33 | ## Description 34 | 35 | **EthLogSpy** is a Golang Reverse Proxy for an Ethereum Node. 36 | 37 | It exposes the given node to the world by just reverse proxying every JSON RPC call, except for the `eth_getLogs` one: on this method EthLogSpy retrieves the logs stored in the database (**MongoDB**) or in the cache (**Redis**) and returns it in the same format that the Ethereum Node would use, basically faking its response. 38 | 39 | It is fully compatible with the `web3js` library, so your dApp can easily retrieve the logs just by using the `web3` instance (thus avoiding making any specific HTTP request to EthLogSpy). 40 | 41 | The yaml configuration file is layed out as follows: 42 | 43 | ```yaml 44 | mongo: 45 | connection: "mongodb://localhost:27017" # mongodb connection string 46 | db_name: "ethlogspy" # mongodb database name 47 | redis: 48 | host: "localhost" # redis host 49 | port: 6379 # redis port 50 | password: "" # redis password 51 | db: 0 # redis db 52 | ``` 53 | 54 | The following environment variables are used: 55 | 56 | ```bash 57 | NODE_HOST = # host name of the ethereum node (default: "localhost") 58 | NODE_PORT = # port of the ethereum node (default: 8545) 59 | CORS_ORIGIN = # allowed cors origins (default: "*") 60 | BLOCK_NUMBER = # block number where to start the retrieval (default: 0) 61 | ``` 62 | 63 | --- 64 | 65 | ## Features 66 | 67 | - [x] HTTP JSON RPC API; 68 | - [x] WebSocket JSON RPC API; 69 | - [x] Support for `eth_subscribe` JSON RPC method; 70 | - [x] CORS; 71 | - [ ] Intel mode for analytics; 72 | - [ ] Disable/enable Redis cache via configuration; 73 | - [ ] Disable/enable in-memory cache via configuration; 74 | - [ ] Logs integrity check (via routine or after every call?). 75 | 76 | --- 77 | 78 | ## Install 79 | 80 | ### Standalone 81 | 82 | If you have `golang` installed on your machine you can simply build the executable by running the following command inside the cloned directory: 83 | 84 | ```bash 85 | go build -o ethlogspy *.go 86 | ``` 87 | 88 | This will create the `ethlogspy` executable that you can run using this commands: 89 | ```bash 90 | ./ethlogspy # default configuration file must be found in /usr/local/ethlogspy/configs/config.yml 91 | ./ethlogspy -c CONFIG_PATH # retrieves the config file from the given CONFIG_PATH 92 | ``` 93 | 94 | ### Docker 95 | 96 | In order to install `ethlogspy` using Docker you need to have a running **MongoDB** and **Redis** instance. After you've cloned this repo, you need to go and update your desired config in the `configs/` folder based on your environment. 97 | 98 | Finally, build the docker image and run it: 99 | 100 | ```bash 101 | docker build -t ethlogspy . 102 | docker run -it --rm -d -p 8080:8080 --name ethlogspy ethlogspy:latest 103 | ``` 104 | 105 | If you're also running MongoDB and Redis on a container, make sure to link them to the `ethlogspy` one. If your Ethereum Node is running locally, make sure also to allow the `ethlogspy` container to contact it by using the proper Docker configuration. 106 | 107 | For **MacOS** and **Windows** just use the `host.docker.internal` instead of `localhost` in the configuration to reach your node; on **Linux** you need to update the `docker run` command and add the host network flag (`--network="host"`). 108 | 109 | ### Docker Compose 110 | 111 | If you want to run everyting using docker-compose, you simply just clone this repo and then run inside the folder: 112 | 113 | ```bash 114 | docker-compose build 115 | nohup docker-compose up & 116 | ``` 117 | 118 | This will build and run a local instance of MongoDB, Redis and EthLogSpy, ready for use. 119 | 120 | --- 121 | 122 | ## DAppNode 123 | 124 | EthLogSpy can be installed on a **DAppNode** with one the following architectures: 125 | - `linux/amd64` 126 | - `linux/arm64` 127 | 128 | The package is not available yet on the official **DAppStore**, but soon it will be. In order to install it on a DAppNode you must be connected either to the DAppNode Wi-Fi or its VPN and have the `@dappnode/dappnodesdk` installed on your computer; you can follow the instructions at the link or simply run on your terminal the following line: 129 | 130 | ```bash 131 | npm install -g @dappnode/dappnodesdk 132 | ``` 133 | 134 | After you've installed the SDK, clone this repository and run the `dappnodesdk build` command. After everything it's finished, you'll receive an IPFS link to install the EthLogSpy (+ MongoDB + Redis) containers on your node. 135 | 136 | --- 137 | 138 | ## Example 139 | 140 | These are just few examples of how you easily you can use **web3js** in conjunction with a running **EthLogSpy** instance: 141 | 142 | ```javascript 143 | const Web3 = require('web3'); 144 | 145 | const web3 = new Web3("http://localhost:8545"); // no ethlogspy and node on 8545 146 | const web3 = new Web3("http://localhost:8080"); // with ethlogspy on 8080 pointing to node on 8545 147 | 148 | const result = await web3.eth.getPastLogs({ fromBlock: 0 }); // retrieve the logs 149 | ``` 150 | 151 | Having a EthLogSpy instance in front of your node doesn't mean, of course, that you can't use all the other RPC APIs: 152 | 153 | ```javascript 154 | const accounts = await web3.eth.getAccounts(); // can you retrieve the accounts? yes. 155 | const receipt = await web3.eth.sendTransaction(accounts[0], accounts[1], 1000000000); // can you send a transaction? yes. 156 | const contract = new web3.eth.Contract(jsonInterface, address); // can you get a contract? yes. 157 | ``` 158 | 159 | What kind of spy doesn't allow all of this? 160 | 161 | --- 162 | 163 | ## Security considerations 164 | 165 | Please, if you're running EthLogSpy with MongoDB and Redis on the same machine, do not expose to the public the `27017` and `6379` ports (or whatever ports you did choose for MongoDB and Redis) or at least, if you do, make sure to edit the `docker-compose.yml` properly and the `config.yml` file and add some authentication to both the db and the cache. 166 | 167 | MongoDB becomes the source of truth for all the logs that can be retrieved through your node, so make sure the following: 168 | - Do not expose its port publicly without any security mechanism; 169 | - Do not delete the persistent volume where the db is stored if you don't want to sync again; 170 | - Do not manually edit any of the logs inside of the database: this could cause some inconsistency between the real Ethereum Blockchain state and your EthLogSpy instance. 171 | 172 | --- 173 | 174 | ## Contributing 175 | 176 | We welcome community contributions! 177 | 178 | Please check out our open issues to get started. 179 | 180 | If you discover something that could potentially impact security, please notify us immediately by sending an e-mail at paolo.rollo1997@gmail.com. We'll get in touch with you as fast as we can! 181 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/AdhityaRamadhanus/fasthttpcors v0.0.0-20170121111917-d4c07198763a h1:XVdatQFSP2YhJGjqLLIfW8QBk4loz/SCe/PxkXDiW+s= 2 | github.com/AdhityaRamadhanus/fasthttpcors v0.0.0-20170121111917-d4c07198763a/go.mod h1:C0A1KeiVHs+trY6gUTPhhGammbrZ30ZfXRW/nuT7HLw= 3 | github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= 4 | github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc= 5 | github.com/Azure/azure-storage-blob-go v0.7.0/go.mod h1:f9YQKtsG1nMisotuTPpO0tjNuEjKRYAcJU8/ydDI++4= 6 | github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= 7 | github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= 8 | github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc= 9 | github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= 10 | github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= 11 | github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= 12 | github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= 13 | github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM= 14 | github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= 15 | github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= 16 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 17 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 18 | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 19 | github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8= 20 | github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= 21 | github.com/VictoriaMetrics/fastcache v1.5.7 h1:4y6y0G8PRzszQUYIQHHssv/jgPHAb5qQuuDNdCbyAgw= 22 | github.com/VictoriaMetrics/fastcache v1.5.7/go.mod h1:ptDBkNMQI4RtmVo8VS/XwRY6RoTu1dAWCbrk+6WsEM8= 23 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 24 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 25 | github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= 26 | github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= 27 | github.com/andybalholm/brotli v1.0.0 h1:7UCwP93aiSfvWpapti8g88vVVGp2qqtGyePsSuDafo4= 28 | github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= 29 | github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847 h1:rtI0fD4oG/8eVokGVPYJEW1F88p1ZNgXiEIs9thEE4A= 30 | github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ= 31 | github.com/aws/aws-sdk-go v1.25.48/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= 32 | github.com/aws/aws-sdk-go v1.34.28 h1:sscPpn/Ns3i0F4HPEWAVcwdIRaZZCuL7llJ2/60yPIk= 33 | github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48= 34 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 35 | github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6 h1:Eey/GGQ/E5Xp1P2Lyx1qj007hLZfbi0+CoVeJruGCtI= 36 | github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ= 37 | github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= 38 | github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= 39 | github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= 40 | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 41 | github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= 42 | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 43 | github.com/cloudflare/cloudflare-go v0.10.2-0.20190916151808-a80f83b9add9/go.mod h1:1MxXX1Ux4x6mqPmjkUgTP1CdXIBXKX7T+Jk9Gxrmx+U= 44 | github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 45 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 46 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 47 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 48 | github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea h1:j4317fAZh7X6GqbFowYdYdI0L9bwxL07jyPZIdepyZ0= 49 | github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= 50 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 51 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= 52 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= 53 | github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= 54 | github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= 55 | github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= 56 | github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA= 57 | github.com/dvyukov/go-fuzz v0.0.0-20200318091601-be3528f3a813/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= 58 | github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c h1:JHHhtb9XWJrGNMcrVP6vyzO4dusgi/HnceHTgxSejUM= 59 | github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= 60 | github.com/ethereum/go-ethereum v1.9.24 h1:6AK+ORt3EMDO+FTjzXy/AQwHMbu52J2nYHIjyQX9azQ= 61 | github.com/ethereum/go-ethereum v1.9.24/go.mod h1:JIfVb6esrqALTExdz9hRYvrP0xBDf6wCncIu1hNwHpM= 62 | github.com/fasthttp/websocket v1.4.1 h1:fisgNMCNCbIPM5GRRRTAckRrynbSzf76fevcJYJYnSM= 63 | github.com/fasthttp/websocket v1.4.1/go.mod h1:toetUvZ3KISxtZERe0wzPPpnaN8GZCKHCowWctwA50o= 64 | github.com/fatih/color v1.3.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 65 | github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc h1:jtW8jbpkO4YirRSyepBOH8E+2HEw6/hKkBvFPwhUN8c= 66 | github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= 67 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 68 | github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= 69 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 70 | github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI= 71 | github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= 72 | github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0= 73 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 74 | github.com/go-logfmt/logfmt v0.3.0 h1:8HUsc87TaSWLKwrnumgC8/YconD2fJQsRJAsWaPg2ic= 75 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 76 | github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E= 77 | github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= 78 | github.com/go-redis/redis/v8 v8.4.0 h1:J5NCReIgh3QgUJu398hUncxDExN4gMOHI11NVbVicGQ= 79 | github.com/go-redis/redis/v8 v8.4.0/go.mod h1:A1tbYoHSa1fXwN+//ljcCYYJeLmVrwL9hbQN45Jdy0M= 80 | github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= 81 | github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= 82 | github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= 83 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 84 | github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= 85 | github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= 86 | github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= 87 | github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= 88 | github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= 89 | github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= 90 | github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= 91 | github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= 92 | github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= 93 | github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= 94 | github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= 95 | github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= 96 | github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= 97 | github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= 98 | github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= 99 | github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= 100 | github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= 101 | github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= 102 | github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= 103 | github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= 104 | github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= 105 | github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= 106 | github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= 107 | github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= 108 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 109 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 110 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 111 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 112 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 113 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 114 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 115 | github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= 116 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 117 | github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 118 | github.com/golang/snappy v0.0.2-0.20200707131729-196ae77b8a26 h1:lMm2hD9Fy0ynom5+85/pbdkiYcBqM1JWmhpAXLmy0fw= 119 | github.com/golang/snappy v0.0.2-0.20200707131729-196ae77b8a26/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 120 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 121 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 122 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 123 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 124 | github.com/google/go-cmp v0.5.3 h1:x95R7cp+rSeeqAMI2knLtQ0DKlaBhv2NrtrOvafPHRo= 125 | github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 126 | github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 127 | github.com/gorilla/websocket v1.4.1-0.20190629185528-ae1634f6a989 h1:giknQ4mEuDFmmHSrGcbargOuLHQGtywqo4mheITex54= 128 | github.com/gorilla/websocket v1.4.1-0.20190629185528-ae1634f6a989/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= 129 | github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= 130 | github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= 131 | github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= 132 | github.com/holiman/uint256 v1.1.1 h1:4JywC80b+/hSfljFlEBLHrrh+CIONLDz9NuFl0af4Mw= 133 | github.com/holiman/uint256 v1.1.1/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= 134 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 135 | github.com/huin/goupnp v1.0.0 h1:wg75sLpL6DZqwHQN6E1Cfk6mtfzS45z8OV+ic+DtHRo= 136 | github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= 137 | github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= 138 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 139 | github.com/influxdata/influxdb v1.2.3-0.20180221223340-01288bdb0883/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY= 140 | github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458 h1:6OvNmYgJyexcZ3pYbTI9jWx5tHo1Dee/tWbLMfPe2TA= 141 | github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= 142 | github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= 143 | github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= 144 | github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= 145 | github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= 146 | github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= 147 | github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= 148 | github.com/julienschmidt/httprouter v1.1.1-0.20170430222011-975b5c4c7c21/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 149 | github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356 h1:I/yrLt2WilKxlQKCM52clh5rGzTKpVctGT1lH4Dc8Jw= 150 | github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= 151 | github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= 152 | github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= 153 | github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= 154 | github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= 155 | github.com/klauspost/compress v1.10.7 h1:7rix8v8GpI3ZBb0nSozFRgbtXKv+hOe+qfEpZqybrAg= 156 | github.com/klauspost/compress v1.10.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= 157 | github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= 158 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 159 | github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= 160 | github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 161 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= 162 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 163 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 164 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 165 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 166 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 167 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 168 | github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= 169 | github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= 170 | github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= 171 | github.com/mattn/go-colorable v0.1.0 h1:v2XXALHHh6zHfYTJ+cSkwtyffnaOyR1MXaA91mTrb8o= 172 | github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 173 | github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= 174 | github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= 175 | github.com/mattn/go-isatty v0.0.5-0.20180830101745-3fb116b82035 h1:USWjF42jDCSEeikX/G1g40ZWnsPXN5WkZ4jMHZWyBK4= 176 | github.com/mattn/go-isatty v0.0.5-0.20180830101745-3fb116b82035/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 177 | github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= 178 | github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y= 179 | github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= 180 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 181 | github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= 182 | github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= 183 | github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= 184 | github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= 185 | github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= 186 | github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= 187 | github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= 188 | github.com/olekukonko/tablewriter v0.0.2-0.20190409134802-7e037d187b0c h1:1RHs3tNxjXGHeul8z2t6H2N2TlAqpKe5yryJztRx4Jk= 189 | github.com/olekukonko/tablewriter v0.0.2-0.20190409134802-7e037d187b0c/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= 190 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 191 | github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= 192 | github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= 193 | github.com/onsi/ginkgo v1.14.2 h1:8mVmC9kjFFmA8H4pKMUhcblgifdkOIXPvbhN1T36q1M= 194 | github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= 195 | github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= 196 | github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= 197 | github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA= 198 | github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= 199 | github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= 200 | github.com/pborman/uuid v0.0.0-20170112150404-1b00554d8222 h1:goeTyGkArOZIVOMA0dQbyuPWGNQJZGPwPu/QS9GlpnA= 201 | github.com/pborman/uuid v0.0.0-20170112150404-1b00554d8222/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34= 202 | github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= 203 | github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 h1:oYW+YCJ1pachXTQmzR3rNLYGGz4g/UgFcjb28p/viDM= 204 | github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= 205 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 206 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 207 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 208 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 209 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 210 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 211 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 212 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 213 | github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= 214 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 215 | github.com/prometheus/tsdb v0.6.2-0.20190402121629-4f204dcbc150 h1:ZeU+auZj1iNzN8iVhff6M38Mfu73FQiJve/GEXYJBjE= 216 | github.com/prometheus/tsdb v0.6.2-0.20190402121629-4f204dcbc150/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= 217 | github.com/rjeczalik/notify v0.9.1 h1:CLCKso/QK1snAlnhNR/CNvNiFU2saUtjV0bx3EwNeCE= 218 | github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho= 219 | github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 220 | github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 221 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 222 | github.com/rs/cors v0.0.0-20160617231935-a62a804a8a00 h1:8DPul/X0IT/1TNMIxoKLwdemEOBBHDC/K4EB16Cw5WE= 223 | github.com/rs/cors v0.0.0-20160617231935-a62a804a8a00/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= 224 | github.com/rs/xhandler v0.0.0-20160618193221-ed27b6fd6521 h1:3hxavr+IHMsQBrYUPQM5v0CgENFktkkbg1sfpgM3h20= 225 | github.com/rs/xhandler v0.0.0-20160618193221-ed27b6fd6521/go.mod h1:RvLn4FgxWubrpZHtQLnOf6EwhN2hEMusxZOhcW9H3UQ= 226 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 227 | github.com/savsgio/gotils v0.0.0-20190714152828-365999d0a274 h1:F52t1X2ziOrMcQMVHo8ZxwOrDTMAq6MrlKtL1Atu2wU= 228 | github.com/savsgio/gotils v0.0.0-20190714152828-365999d0a274/go.mod h1:w803/Fg1m0hrp1ZT9KNfQe4E4+WOMMFLcgzPvOcye10= 229 | github.com/shirou/gopsutil v2.20.5+incompatible h1:tYH07UPoQt0OCQdgWWMgYHy3/a9bcxNpBIysykNIP7I= 230 | github.com/shirou/gopsutil v2.20.5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= 231 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 232 | github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 233 | github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= 234 | github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= 235 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 236 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 237 | github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= 238 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 239 | github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4 h1:Gb2Tyox57NRNuZ2d3rmvB3pcmbu7O1RS3m8WRx7ilrg= 240 | github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= 241 | github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570 h1:gIlAHnH1vJb5vwEjIp5kBj/eu99p/bl0Ay2goiPe5xE= 242 | github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570/go.mod h1:8OR4w3TdeIHIh1g6EMY5p0gVNOovcWC+1vpc7naMuAw= 243 | github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 h1:njlZPzLwU639dk2kqnCPPv+wNjq7Xb6EfUxe/oX0/NM= 244 | github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3/go.mod h1:hpGUWaI9xL8pRQCTXQgocU38Qw1g0Us7n5PxxTwTCYU= 245 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 246 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 247 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 248 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 249 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 250 | github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= 251 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 252 | github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca h1:Ld/zXl5t4+D69SiV4JoN7kkfvJdOWlPpfxrzxpLMoUk= 253 | github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM= 254 | github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= 255 | github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= 256 | github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef h1:wHSqTBrZW24CsNJDfeh9Ex6Pm0Rcpc7qrgKBiL44vF4= 257 | github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= 258 | github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= 259 | github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= 260 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= 261 | github.com/valyala/fasthttp v1.4.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s= 262 | github.com/valyala/fasthttp v1.17.0 h1:P8/koH4aSnJ4xbd0cUUFEGQs3jQqIxoDDyRQrUiAkqg= 263 | github.com/valyala/fasthttp v1.17.0/go.mod h1:jjraHZVbKOXftJfsOYoAjaeygpj5hr8ermTRJNroD7A= 264 | github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= 265 | github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208 h1:1cngl9mPEoITZG8s8cVcUy5CeIBYhEESkOB7m6Gmkrk= 266 | github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208/go.mod h1:IotVbo4F+mw0EzQ08zFqg7pK3FebNXpaMsRy2RT+Ees= 267 | github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c h1:u40Z8hqBAAQyv+vATcGgV0YCnDjqSL7/q/JyPhhJSPk= 268 | github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= 269 | github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc h1:n+nNi93yXLkJvKwXNP9d55HC7lGK4H/SRcwB5IaUZLo= 270 | github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= 271 | github.com/yeqown/fasthttp-reverse-proxy v0.0.0-20200930023507-ed73ac32bc64 h1:4dmnKp9YO5u6MACmB2p4FrEtDV8MNx438A9/N0kPIfg= 272 | github.com/yeqown/fasthttp-reverse-proxy v0.0.0-20200930023507-ed73ac32bc64/go.mod h1:hgX6t+JE03QfTU4mpF0ls1FJa+Y/49wcG7++6d6apu8= 273 | github.com/yeqown/log v1.0.3 h1:kWwBMytMSkYkr2fmi20PLgDuJNXkq3mOpVbfsTS1soI= 274 | github.com/yeqown/log v1.0.3/go.mod h1:RTslXFTg+8Uj5AizIxdfichvBZi/OOKao6yP3tMtTns= 275 | go.mongodb.org/mongo-driver v1.4.3 h1:moga+uhicpVshTyaqY9L23E6QqwcHRUv1sqyOsoyOO8= 276 | go.mongodb.org/mongo-driver v1.4.3/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc= 277 | go.opentelemetry.io/otel v0.14.0 h1:YFBEfjCk9MTjaytCNSUkp9Q8lF7QJezA06T71FbQxLQ= 278 | go.opentelemetry.io/otel v0.14.0/go.mod h1:vH5xEuwy7Rts0GNtsCW3HYQoZDY+OmBJ6t1bFGGlxgw= 279 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 280 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 281 | golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= 282 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 283 | golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 284 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 285 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= 286 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 287 | golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= 288 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 289 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 290 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 291 | golang.org/x/mobile v0.0.0-20200801112145-973feb4309de/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4= 292 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 293 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 294 | golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 295 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 296 | golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 297 | golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 298 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 299 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 300 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 301 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 302 | golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 303 | golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 304 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 305 | golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 306 | golang.org/x/net v0.0.0-20201016165138-7b1cca2348c0 h1:5kGOVHlq0euqwzgTC9Vu15p6fV1Wi0ArVi8da2urnVg= 307 | golang.org/x/net v0.0.0-20201016165138-7b1cca2348c0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 308 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 309 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 310 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 311 | golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 312 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 313 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= 314 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 315 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 316 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 317 | golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 318 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 319 | golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 320 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 321 | golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 322 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 323 | golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 324 | golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 325 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 326 | golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 327 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 328 | golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 329 | golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 330 | golang.org/x/sys v0.0.0-20200824131525-c12d262b63d8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 331 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= 332 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 333 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 334 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 335 | golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= 336 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 337 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= 338 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 339 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 340 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 341 | golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 342 | golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 343 | golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 344 | golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 345 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 346 | golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 347 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 348 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 349 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 350 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= 351 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 352 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 353 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 354 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 355 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 356 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 357 | google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= 358 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 359 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 360 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 361 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= 362 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 363 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 364 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 365 | gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= 366 | gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= 367 | gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6 h1:a6cXbcDDUkSBlpnkWV1bJ+vv3mOgQEltEJ2rPxroVu0= 368 | gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns= 369 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= 370 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 371 | gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0= 372 | gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0= 373 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 374 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 375 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 376 | gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= 377 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 378 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 379 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 380 | gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= 381 | --------------------------------------------------------------------------------