├── .dockerignore ├── .github └── workflows │ └── main.yml ├── .gitignore ├── Dockerfile ├── LICENSE ├── Makefile ├── Readme.md ├── admin └── server.go ├── common └── types.go ├── config └── config.json ├── docs ├── assets │ └── cross-chain.png ├── config.md ├── err_handling.md └── oracle_relayer.md ├── executor ├── bbc │ └── executor.go ├── bsc │ ├── abi │ │ ├── CrossChain.abi │ │ ├── CrossChain.go │ │ └── CrossChain.sol │ ├── executor.go │ └── types.go ├── executor.go └── mock │ └── mock_executor.go ├── go.mod ├── go.sum ├── main.go ├── model └── model.go ├── observer ├── observer.go └── observer_test.go ├── relayer ├── relayer.go └── relayer_test.go └── util ├── aws.go ├── config.go ├── config_test.go ├── log.go ├── test_util.go └── util.go /.dockerignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: CI 4 | 5 | # Controls when the action will run. Triggers the workflow on push or pull request 6 | # events but only for the master branch 7 | on: 8 | push: 9 | branches: [ master ] 10 | pull_request: 11 | branches: [ master ] 12 | 13 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 14 | jobs: 15 | # This workflow contains a single job called "build" 16 | build: 17 | # The type of runner that the job will run on 18 | runs-on: ubuntu-latest 19 | 20 | # Steps represent a sequence of tasks that will be executed as part of the job 21 | steps: 22 | - name: Set up Go 1.19 23 | uses: actions/setup-go@v2.1.3 24 | with: 25 | go-version: 1.19 26 | 27 | - name: Check out code into the Go module directory 28 | uses: actions/checkout@v2.1.0 29 | 30 | - name: Run Tests 31 | run: make test 32 | 33 | - name: Build 34 | run: make build -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea/* 3 | build/* 4 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.19-alpine as build 2 | 3 | # Set up apk dependencies 4 | ENV PACKAGES make git libc-dev bash gcc linux-headers eudev-dev curl ca-certificates 5 | 6 | # Set working directory for the build 7 | WORKDIR /opt/app 8 | 9 | # Add source files 10 | COPY . . 11 | 12 | # Install minimum necessary dependencies, remove packages 13 | RUN apk add --no-cache $PACKAGES && \ 14 | make build 15 | 16 | FROM alpine:3.17 17 | 18 | WORKDIR /opt/app 19 | RUN mkdir /opt/app/config 20 | 21 | COPY config/config.json /opt/app/config/config.json 22 | COPY --from=build /opt/app/build/relayer /opt/app/relayer 23 | 24 | ENV RELAYER_HOME /opt/app 25 | 26 | ENV BBC_NETWORK 1 27 | ENV CONFIG_FILE_PATH $RELAYER_HOME/config/config.json 28 | ENV CONFIG_TYPE "local" 29 | # You need to specify aws s3 config if you want to load config from s3 30 | ENV AWS_REGION "" 31 | ENV AWS_SECRET_KEY "" 32 | 33 | # Run as non-root user for security 34 | USER 1000 35 | 36 | VOLUME [ $RELAYER_HOME ] 37 | 38 | # Run the app 39 | CMD ./relayer --bbc-network $BBC_NETWORK --config-type $CONFIG_TYPE --config-path $CONFIG_FILE_PATH --aws-region $AWS_REGION --aws-secret-key $AWS_SECRET_KEY -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2020 All BNB Chain Developers. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | BUILD_TAGS = netgo 2 | PACKAGES=$(shell go list ./...) 3 | 4 | build: 5 | ifeq ($(OS),Windows_NT) 6 | go build $(BUILD_FLAGS) -o build/relayer.exe main.go 7 | else 8 | go build $(BUILD_FLAGS) -o build/relayer main.go 9 | endif 10 | 11 | build_docker: build 12 | docker build --tag oracle_relayer . 13 | 14 | test: 15 | make set_with_deadlock 16 | make test_unit 17 | make cleanup_after_test_with_deadlock 18 | 19 | test_unit: 20 | @echo "--> go test " 21 | @go test --cover -race $(PACKAGES) 22 | 23 | # uses https://github.com/sasha-s/go-deadlock/ to detect potential deadlocks 24 | set_with_deadlock: 25 | go get github.com/sasha-s/go-deadlock 26 | 27 | find . -name "*.go" | xargs -n 1 sed -i.mutex_bak 's/sync.RWMutex/deadlock.RWMutex/' 28 | find . -name "*.go" | xargs -n 1 sed -i.mutex_bak 's/sync.Mutex/deadlock.Mutex/' 29 | go mod download 30 | 31 | # cleanes up after you ran test_with_deadlock 32 | cleanup_after_test_with_deadlock: 33 | find . -name "*.go" | xargs -n 1 sed -i.mutex_bak 's/deadlock.RWMutex/sync.RWMutex/' 34 | find . -name "*.go" | xargs -n 1 sed -i.mutex_bak 's/deadlock.Mutex/sync.Mutex/' 35 | find . -name "*.go.mutex_bak" | xargs rm 36 | 37 | update_mock_executor: 38 | go get github.com/golang/mock/gomock 39 | go install github.com/golang/mock/mockgen 40 | $(shell mockgen -source=executor/executor.go -package mock > executor/mock/mock_executor.go) 41 | 42 | .PHONY: build build_docker test test_unit set_with_deadlock cleanup_after_test_with_deadlock update_mock_executor 43 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Oracle Relayer 2 | 3 | Oracle Relayer is responsible for relaying events of bsc to BNB Beacon Beacon chain. 4 | 5 | ## Build 6 | 7 | Build binary: 8 | 9 | ```shell script 10 | $ make build 11 | ``` 12 | 13 | Build docker image: 14 | 15 | ```shell script 16 | $ make build_docker 17 | ``` 18 | 19 | ## Config 20 | 21 | There is a config template in config directory, you should create your own config to run your relayer correctly. 22 | You can refer to [config doc](./docs/config.md) for more details. 23 | 24 | ### Recommendations 25 | If you are going to deploy your oracle relayer in production, AWS Secret Manager is recommended. You can use AWS Secret 26 | Manager to host your mnemonic. 27 | 28 | For BBC and BSC providers, you should use trusted nodes and TLS connection is recommended. 29 | 30 | ## Run 31 | 32 | Run locally: 33 | 34 | ```shell script 35 | $ ./build/relayer --bbc-network [0 for testnet, 1 for mainnet] --config-type [local or aws] --config-path config_file_path --aws-region [aws region or omit] --aws-secret-key [aws secret key for config or omit] 36 | ``` 37 | 38 | Run docker: 39 | ```shell script 40 | $ docker run -it -v /your/data/path:/relayer -e BBC_NETWORK={0 or 1} -e CONFIG_TYPE="local" -e CONFIG_FILE_PATH=/your/config/file/path/in/container -d oracle_relayer 41 | ``` 42 | 43 | ## License 44 | 45 | Distributed under the [GNU Lesser General Public License v3.0](https://www.gnu.org/licenses/lgpl-3.0.en.html). See [LICENSE](LICENSE) for more information. 46 | -------------------------------------------------------------------------------- /admin/server.go: -------------------------------------------------------------------------------- 1 | package admin 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/http" 7 | "time" 8 | 9 | "github.com/gorilla/mux" 10 | 11 | "github.com/binance-chain/oracle-relayer/executor/bbc" 12 | "github.com/binance-chain/oracle-relayer/util" 13 | ) 14 | 15 | const ( 16 | DefaultListenAddr = "0.0.0.0:8080" 17 | ) 18 | 19 | type Admin struct { 20 | Config *util.Config 21 | BBCExecutor *bbc.Executor 22 | } 23 | 24 | func NewAdmin(config *util.Config, executor *bbc.Executor) *Admin { 25 | return &Admin{ 26 | Config: config, 27 | BBCExecutor: executor, 28 | } 29 | } 30 | 31 | func (admin *Admin) Endpoints(w http.ResponseWriter, r *http.Request) { 32 | endpoints := struct { 33 | Endpoints []string `json:"endpoints"` 34 | }{ 35 | Endpoints: []string{}, 36 | } 37 | 38 | jsonBytes, err := json.MarshalIndent(endpoints, "", " ") 39 | if err != nil { 40 | http.Error(w, err.Error(), http.StatusInternalServerError) 41 | return 42 | } 43 | 44 | w.Header().Set("Content-Type", "application/json") 45 | w.WriteHeader(http.StatusOK) 46 | _, err = w.Write(jsonBytes) 47 | if err != nil { 48 | util.Logger.Errorf("write response error, err=%s", err.Error()) 49 | } 50 | } 51 | 52 | func (admin *Admin) Serve() { 53 | router := mux.NewRouter() 54 | 55 | router.HandleFunc("/", admin.Endpoints) 56 | 57 | listenAddr := DefaultListenAddr 58 | if admin.Config.AdminConfig != nil && admin.Config.AdminConfig.ListenAddr != "" { 59 | listenAddr = admin.Config.AdminConfig.ListenAddr 60 | } 61 | srv := &http.Server{ 62 | Handler: router, 63 | Addr: listenAddr, 64 | WriteTimeout: 15 * time.Second, 65 | ReadTimeout: 15 * time.Second, 66 | } 67 | 68 | util.Logger.Infof("start admin server at %s", srv.Addr) 69 | 70 | err := srv.ListenAndServe() 71 | if err != nil { 72 | panic(fmt.Sprintf("start admin server error, err=%s", err.Error())) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /common/types.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import "time" 4 | 5 | const ( 6 | ObserverMaxBlockNumber = 10000 7 | ObserverPruneInterval = 10 * time.Second 8 | ObserverAlertInterval = 5 * time.Second 9 | ObserverFetchInterval = 2 * time.Second 10 | 11 | PackageDelayAlertInterval = 5 * time.Second 12 | 13 | DefaultConfirmNum int64 = 15 14 | ) 15 | 16 | const ( 17 | DBDialectMysql = "mysql" 18 | DBDialectSqlite3 = "sqlite3" 19 | ) 20 | 21 | type BlockAndPackageLogs struct { 22 | Height int64 23 | BlockHash string 24 | ParentBlockHash string 25 | BlockTime int64 26 | Packages []interface{} 27 | } 28 | -------------------------------------------------------------------------------- /config/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "db_config": { 3 | "dialect": "mysql", 4 | "db_path": "user:password@(host:port)/db_name?charset=utf8&parseTime=True&loc=Local" 5 | }, 6 | "chain_config": { 7 | "bsc_start_height": 1, 8 | "bsc_providers": ["bsc_provider"], 9 | "bsc_confirm_num": 2, 10 | "bsc_cross_chain_contract_address": "0x0000000000000000000000000000000000001004", 11 | 12 | "bbc_rpc_addrs": ["bbc_rpc_addr"], 13 | "bbc_key_type": "mnemonic", 14 | "bbc_aws_region": "", 15 | "bbc_aws_secret_name": "", 16 | "bbc_mnemonic": "", 17 | 18 | "relay_interval": 1000 19 | }, 20 | "log_config": { 21 | "level": "INFO", 22 | "filename": "", 23 | "max_file_size_in_mb": 0, 24 | "max_backups_of_log_files": 0, 25 | "max_age_to_retain_log_files_in_days": 0, 26 | "use_console_logger": true, 27 | "use_file_logger": false, 28 | "compress": false 29 | }, 30 | "admin_config": { 31 | "listen_addr": ":8080" 32 | }, 33 | "alert_config": { 34 | "moniker": "moniker", 35 | "telegram_bot_id": "", 36 | "telegram_chat_id": "", 37 | "pager_duty_auth_token": "", 38 | "block_update_time_out": 60, 39 | "package_delay_alert_threshold": 30 40 | } 41 | } -------------------------------------------------------------------------------- /docs/assets/cross-chain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bnb-chain/oracle-relayer/6adc55ae57e4b7fe68e038dd92a1132b9b01ea9d/docs/assets/cross-chain.png -------------------------------------------------------------------------------- /docs/config.md: -------------------------------------------------------------------------------- 1 | ## DB config 2 | 3 | DB config is config of database. 4 | 5 | + dialect: it should be `sqlite3` or `mysql`, only sqlite and mysql are supported for now. 6 | + db_path: db file path or mysql db config, eg(`root:12345678@(127.0.0.1:3306)/relayer?charset=utf8&parseTime=True&loc=Local`). 7 | 8 | ## Alert config 9 | 10 | Relayer will send alert messages to telegram group if block is not be fetched for a long time or tx sent is failed. 11 | 12 | + moniker: `moniker` is moniker for relayer. 13 | + telegram_bot_id: `telegram_bot_id` is your telegram bot id. 14 | + telegram_chat_id: `telegram_chat_id` is chat id of group your bot joined. 15 | + block_update_time_out: `bnb_block_update_time_out` is how long(in seconds) that block is not be fetched in bsc chain you want 16 | relayer to send alert messages. 17 | 18 | References: 19 | + [create a bot](https://core.telegram.org/bots#6-botfather) 20 | + [get bot id and chat id](https://stackoverflow.com/questions/32423837/telegram-bot-how-to-get-a-group-chat-id) 21 | 22 | ## Chain config 23 | 24 | Chain common config for deputy. Pls note that `swap_amount` and `fixed_fee` below are number with decimal. For example, decimal in BNB chain 25 | is 8 which means 100000000 is 1 actually. You need to handle decimal and amount with decimal. 26 | 27 | + bsc_start_height: height of bsc chain you want to start syncing when you start your relayer. 28 | + bsc_providers: array of provider address of bsc chain. 29 | + bsc_confirm_num: confirm number of bsc chain. 30 | + bsc_token_hub_contract_address: token hub contract address of bsc. 31 | + bsc_validator_set_contract_address: validator set contract address of bsc. 32 | 33 | + bbc_rpc_addrs: array of rpc address of bbc. 34 | + bbc_key_type: `mnemonic` and `aws_mnemonic` supported. `mnemonic` will use mnemonic provided below and `aws_mnemonic` 35 | will fetch mnemonic from aws secret manager. 36 | + bbc_aws_region: region of aws. 37 | + bbc_aws_secret_name: secret name of private key in aws. 38 | + bbc_mnemonic: mnemonic of relayer operator. 39 | 40 | ## Log config 41 | 42 | + level: level of log, `CRITICAL`,`ERROR`,`WARNING`,`NOTICE`,`INFO`,`DEBUG` are supported. 43 | + filename: log file path if `use_console_logger` is true 44 | + max_file_size_in_mb: max log file size 45 | + max_backups_of_log_files: max backups of log files 46 | + max_age_to_retain_log_files_in_days: max days to retain log files 47 | + use_console_logger: use console logger or not 48 | + use_file_logger: use file logger or not 49 | + compress: compress log file or not 50 | -------------------------------------------------------------------------------- /docs/err_handling.md: -------------------------------------------------------------------------------- 1 | # Critical Error Handling 2 | 3 | ## Dropped cross-chain packages 4 | 5 | Since the cross-chain packages are relayed to BNB Beacon Chain grouped by `OracleSequence`. 6 | 7 | The `OracleSequence` is increased one by one in BNB Beacon Chain, so there is not possible to 8 | skip `OracleSequence`. But it is possible that some packages are dropped by relayers. 9 | 10 | The error can't be detected immediately util BNB Beacon Chain finds that there are some 11 | `PackageSequence` missing. 12 | 13 | When that happens, the relayers needs to add the missing packages to the next group 14 | manually so that every thing will be back to normal and BNB Beacon Chain will not complain 15 | missing `PackageSequence` any longer. -------------------------------------------------------------------------------- /docs/oracle_relayer.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | ![](./assets/cross-chain.png) 4 | 5 | Oracle Relayer is responsible for relaying events of BNB Smart Chain to BNB Chain. 6 | 7 | ## Design 8 | 9 | Oracle Relayer fetches blocks from BSC one by one and save `crossChainPackage` events to 10 | database. Once the block of events is confirmed by BSC for a number of 2/3 validator amount blocks, 11 | Oracle relayer will relay the events to BNB Beacon Chain. 12 | 13 | There are two sequence related to cross-chain events: 14 | + `OracleSequence`: `OracleSequence` is used to control the batch length of events to be relayed to 15 | BNB Beacon Chain. For example, there are `n` cross-chain events in one block, and the batch length of events 16 | is `m`(which is controlled by BSC), there may be `n/m+1` unique `OracleSequence` in those events so that 17 | Oracle Relayer can relay all the events to BNB Beacon Chain in `n/m+1` batches. To sum up, Oracle Relayer will 18 | relay all the events with the same `OracleSequence` to BNB Beacon Chain in one batch and `OracleSequence` is increased 19 | one by one and will be checked in BNB Beacon Chain. 20 | + `PackageSequence`: `PackageSequence` is the sequence of each event type which is also increased 21 | one by one. 22 | 23 | To improve the performance of cross-chain communication, Oracle Relayer relays a batch of 24 | events which with the same `OracleSequence` to BNB Beacon Chain instead of one by one. -------------------------------------------------------------------------------- /executor/bbc/executor.go: -------------------------------------------------------------------------------- 1 | package bbc 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | 8 | "github.com/binance-chain/oracle-relayer/util" 9 | "github.com/bnb-chain/go-sdk/client/rpc" 10 | "github.com/bnb-chain/go-sdk/common/types" 11 | "github.com/bnb-chain/go-sdk/keys" 12 | "github.com/bnb-chain/go-sdk/types/msg" 13 | ) 14 | 15 | type Executor struct { 16 | config *util.Config 17 | RpcClients []rpc.Client 18 | } 19 | 20 | // NewExecutor returns the BBC executor instance 21 | func NewExecutor(providers []string, network types.ChainNetwork, cfg *util.Config) (*Executor, error) { 22 | return &Executor{ 23 | config: cfg, 24 | RpcClients: initClients(providers, network), 25 | }, nil 26 | } 27 | 28 | // getKeyManager returns the key manager from config 29 | func getKeyManager(config *util.ChainConfig) (keys.KeyManager, error) { 30 | var bnbMnemonic string 31 | if config.BBCKeyType == util.KeyTypeAWSMnemonic { 32 | awsMnemonic, err := util.GetSecret(config.BBCAWSSecretName, config.BBCAWSRegion) 33 | if err != nil { 34 | return nil, err 35 | } 36 | bnbMnemonic = awsMnemonic 37 | } else { 38 | bnbMnemonic = config.BBCMnemonic 39 | } 40 | 41 | return keys.NewMnemonicKeyManager(bnbMnemonic) 42 | } 43 | 44 | func initClients(providers []string, network types.ChainNetwork) []rpc.Client { 45 | clients := make([]rpc.Client, 0) 46 | 47 | for _, provider := range providers { 48 | client := rpc.NewRPCClient(provider, network) 49 | clients = append(clients, client) 50 | } 51 | 52 | return clients 53 | } 54 | 55 | func (e *Executor) getClient() rpc.Client { 56 | r := rand.New(rand.NewSource(time.Now().UnixNano())) 57 | 58 | idx := r.Intn(len(e.RpcClients)) 59 | return e.RpcClients[idx] 60 | } 61 | 62 | // GetAddress returns validator address of the oracle relayer 63 | func (e *Executor) GetAddress() types.ValAddress { 64 | keyManager, err := getKeyManager(e.config.ChainConfig) 65 | if err != nil { 66 | return types.ValAddress{} 67 | } 68 | return types.ValAddress(keyManager.GetAddr()) 69 | } 70 | 71 | // GetProphecy returns the prophecy of the given sequence 72 | func (e *Executor) GetProphecy(chainId uint16, sequence int64) (*msg.Prophecy, error) { 73 | prop, err := e.getClient().GetProphecy(types.IbcChainID(chainId), sequence) 74 | if err != nil { 75 | return nil, err 76 | } 77 | return prop, err 78 | } 79 | 80 | // Claim sends claim to BNB Beacon Chain 81 | func (e *Executor) Claim(chainId uint16, sequence uint64, payload []byte) (string, error) { 82 | client := e.getClient() 83 | 84 | keyManager, err := getKeyManager(e.config.ChainConfig) 85 | if err != nil { 86 | return "", fmt.Errorf("get key manager error, err=%s", err.Error()) 87 | } 88 | client.SetKeyManager(keyManager) 89 | defer client.SetKeyManager(nil) 90 | 91 | res, err := client.Claim(types.IbcChainID(chainId), sequence, payload, rpc.Commit) 92 | if err != nil { 93 | return "", err 94 | } 95 | if res.Code != 0 { 96 | return "", fmt.Errorf("claim error, code=%d, log=%s", res.Code, res.Log) 97 | } 98 | util.Logger.Infof("claim success, tx_hash=%s", res.Hash.String()) 99 | return res.Hash.String(), nil 100 | } 101 | 102 | // GetCurrentSequence return the current oracle sequence of BNB Beacon Chain 103 | func (e *Executor) GetCurrentSequence(chainId uint16) (int64, error) { 104 | sequence, err := e.getClient().GetCurrentOracleSequence(types.IbcChainID(chainId)) 105 | if err != nil { 106 | return 0, err 107 | } 108 | return sequence, nil 109 | } 110 | -------------------------------------------------------------------------------- /executor/bsc/abi/CrossChain.abi: -------------------------------------------------------------------------------- 1 | [{"anonymous":false,"inputs":[{"indexed":false,"name":"chainId","type":"uint16"},{"indexed":true,"name":"oracleSequence","type":"uint64"},{"indexed":true,"name":"packageSequence","type":"uint64"},{"indexed":true,"name":"channelId","type":"uint8"},{"indexed":false,"name":"payload","type":"bytes"}],"name":"crossChainPackage","type":"event"}] -------------------------------------------------------------------------------- /executor/bsc/abi/CrossChain.go: -------------------------------------------------------------------------------- 1 | // Code generated - DO NOT EDIT. 2 | // This file is a generated binding and any manual changes will be lost. 3 | 4 | package abi 5 | 6 | import ( 7 | "errors" 8 | "math/big" 9 | "strings" 10 | 11 | ethereum "github.com/ethereum/go-ethereum" 12 | "github.com/ethereum/go-ethereum/accounts/abi" 13 | "github.com/ethereum/go-ethereum/accounts/abi/bind" 14 | "github.com/ethereum/go-ethereum/common" 15 | "github.com/ethereum/go-ethereum/core/types" 16 | "github.com/ethereum/go-ethereum/event" 17 | ) 18 | 19 | // Reference imports to suppress errors if they are not otherwise used. 20 | var ( 21 | _ = errors.New 22 | _ = big.NewInt 23 | _ = strings.NewReader 24 | _ = ethereum.NotFound 25 | _ = bind.Bind 26 | _ = common.Big1 27 | _ = types.BloomLookup 28 | _ = event.NewSubscription 29 | ) 30 | 31 | // CrossChainMetaData contains all meta data concerning the CrossChain contract. 32 | var CrossChainMetaData = &bind.MetaData{ 33 | ABI: "[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"name\":\"chainId\",\"type\":\"uint16\"},{\"indexed\":true,\"name\":\"oracleSequence\",\"type\":\"uint64\"},{\"indexed\":true,\"name\":\"packageSequence\",\"type\":\"uint64\"},{\"indexed\":true,\"name\":\"channelId\",\"type\":\"uint8\"},{\"indexed\":false,\"name\":\"payload\",\"type\":\"bytes\"}],\"name\":\"crossChainPackage\",\"type\":\"event\"}]", 34 | } 35 | 36 | // CrossChainABI is the input ABI used to generate the binding from. 37 | // Deprecated: Use CrossChainMetaData.ABI instead. 38 | var CrossChainABI = CrossChainMetaData.ABI 39 | 40 | // CrossChain is an auto generated Go binding around an Ethereum contract. 41 | type CrossChain struct { 42 | CrossChainCaller // Read-only binding to the contract 43 | CrossChainTransactor // Write-only binding to the contract 44 | CrossChainFilterer // Log filterer for contract events 45 | } 46 | 47 | // CrossChainCaller is an auto generated read-only Go binding around an Ethereum contract. 48 | type CrossChainCaller struct { 49 | contract *bind.BoundContract // Generic contract wrapper for the low level calls 50 | } 51 | 52 | // CrossChainTransactor is an auto generated write-only Go binding around an Ethereum contract. 53 | type CrossChainTransactor struct { 54 | contract *bind.BoundContract // Generic contract wrapper for the low level calls 55 | } 56 | 57 | // CrossChainFilterer is an auto generated log filtering Go binding around an Ethereum contract events. 58 | type CrossChainFilterer struct { 59 | contract *bind.BoundContract // Generic contract wrapper for the low level calls 60 | } 61 | 62 | // CrossChainSession is an auto generated Go binding around an Ethereum contract, 63 | // with pre-set call and transact options. 64 | type CrossChainSession struct { 65 | Contract *CrossChain // Generic contract binding to set the session for 66 | CallOpts bind.CallOpts // Call options to use throughout this session 67 | TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session 68 | } 69 | 70 | // CrossChainCallerSession is an auto generated read-only Go binding around an Ethereum contract, 71 | // with pre-set call options. 72 | type CrossChainCallerSession struct { 73 | Contract *CrossChainCaller // Generic contract caller binding to set the session for 74 | CallOpts bind.CallOpts // Call options to use throughout this session 75 | } 76 | 77 | // CrossChainTransactorSession is an auto generated write-only Go binding around an Ethereum contract, 78 | // with pre-set transact options. 79 | type CrossChainTransactorSession struct { 80 | Contract *CrossChainTransactor // Generic contract transactor binding to set the session for 81 | TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session 82 | } 83 | 84 | // CrossChainRaw is an auto generated low-level Go binding around an Ethereum contract. 85 | type CrossChainRaw struct { 86 | Contract *CrossChain // Generic contract binding to access the raw methods on 87 | } 88 | 89 | // CrossChainCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. 90 | type CrossChainCallerRaw struct { 91 | Contract *CrossChainCaller // Generic read-only contract binding to access the raw methods on 92 | } 93 | 94 | // CrossChainTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. 95 | type CrossChainTransactorRaw struct { 96 | Contract *CrossChainTransactor // Generic write-only contract binding to access the raw methods on 97 | } 98 | 99 | // NewCrossChain creates a new instance of CrossChain, bound to a specific deployed contract. 100 | func NewCrossChain(address common.Address, backend bind.ContractBackend) (*CrossChain, error) { 101 | contract, err := bindCrossChain(address, backend, backend, backend) 102 | if err != nil { 103 | return nil, err 104 | } 105 | return &CrossChain{CrossChainCaller: CrossChainCaller{contract: contract}, CrossChainTransactor: CrossChainTransactor{contract: contract}, CrossChainFilterer: CrossChainFilterer{contract: contract}}, nil 106 | } 107 | 108 | // NewCrossChainCaller creates a new read-only instance of CrossChain, bound to a specific deployed contract. 109 | func NewCrossChainCaller(address common.Address, caller bind.ContractCaller) (*CrossChainCaller, error) { 110 | contract, err := bindCrossChain(address, caller, nil, nil) 111 | if err != nil { 112 | return nil, err 113 | } 114 | return &CrossChainCaller{contract: contract}, nil 115 | } 116 | 117 | // NewCrossChainTransactor creates a new write-only instance of CrossChain, bound to a specific deployed contract. 118 | func NewCrossChainTransactor(address common.Address, transactor bind.ContractTransactor) (*CrossChainTransactor, error) { 119 | contract, err := bindCrossChain(address, nil, transactor, nil) 120 | if err != nil { 121 | return nil, err 122 | } 123 | return &CrossChainTransactor{contract: contract}, nil 124 | } 125 | 126 | // NewCrossChainFilterer creates a new log filterer instance of CrossChain, bound to a specific deployed contract. 127 | func NewCrossChainFilterer(address common.Address, filterer bind.ContractFilterer) (*CrossChainFilterer, error) { 128 | contract, err := bindCrossChain(address, nil, nil, filterer) 129 | if err != nil { 130 | return nil, err 131 | } 132 | return &CrossChainFilterer{contract: contract}, nil 133 | } 134 | 135 | // bindCrossChain binds a generic wrapper to an already deployed contract. 136 | func bindCrossChain(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { 137 | parsed, err := abi.JSON(strings.NewReader(CrossChainABI)) 138 | if err != nil { 139 | return nil, err 140 | } 141 | return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil 142 | } 143 | 144 | // Call invokes the (constant) contract method with params as input values and 145 | // sets the output to result. The result type might be a single field for simple 146 | // returns, a slice of interfaces for anonymous returns and a struct for named 147 | // returns. 148 | func (_CrossChain *CrossChainRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { 149 | return _CrossChain.Contract.CrossChainCaller.contract.Call(opts, result, method, params...) 150 | } 151 | 152 | // Transfer initiates a plain transaction to move funds to the contract, calling 153 | // its default method if one is available. 154 | func (_CrossChain *CrossChainRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { 155 | return _CrossChain.Contract.CrossChainTransactor.contract.Transfer(opts) 156 | } 157 | 158 | // Transact invokes the (paid) contract method with params as input values. 159 | func (_CrossChain *CrossChainRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { 160 | return _CrossChain.Contract.CrossChainTransactor.contract.Transact(opts, method, params...) 161 | } 162 | 163 | // Call invokes the (constant) contract method with params as input values and 164 | // sets the output to result. The result type might be a single field for simple 165 | // returns, a slice of interfaces for anonymous returns and a struct for named 166 | // returns. 167 | func (_CrossChain *CrossChainCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { 168 | return _CrossChain.Contract.contract.Call(opts, result, method, params...) 169 | } 170 | 171 | // Transfer initiates a plain transaction to move funds to the contract, calling 172 | // its default method if one is available. 173 | func (_CrossChain *CrossChainTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { 174 | return _CrossChain.Contract.contract.Transfer(opts) 175 | } 176 | 177 | // Transact invokes the (paid) contract method with params as input values. 178 | func (_CrossChain *CrossChainTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { 179 | return _CrossChain.Contract.contract.Transact(opts, method, params...) 180 | } 181 | 182 | // CrossChainCrossChainPackageIterator is returned from FilterCrossChainPackage and is used to iterate over the raw logs and unpacked data for CrossChainPackage events raised by the CrossChain contract. 183 | type CrossChainCrossChainPackageIterator struct { 184 | Event *CrossChainCrossChainPackage // Event containing the contract specifics and raw log 185 | 186 | contract *bind.BoundContract // Generic contract to use for unpacking event data 187 | event string // Event name to use for unpacking event data 188 | 189 | logs chan types.Log // Log channel receiving the found contract events 190 | sub ethereum.Subscription // Subscription for errors, completion and termination 191 | done bool // Whether the subscription completed delivering logs 192 | fail error // Occurred error to stop iteration 193 | } 194 | 195 | // Next advances the iterator to the subsequent event, returning whether there 196 | // are any more events found. In case of a retrieval or parsing error, false is 197 | // returned and Error() can be queried for the exact failure. 198 | func (it *CrossChainCrossChainPackageIterator) Next() bool { 199 | // If the iterator failed, stop iterating 200 | if it.fail != nil { 201 | return false 202 | } 203 | // If the iterator completed, deliver directly whatever's available 204 | if it.done { 205 | select { 206 | case log := <-it.logs: 207 | it.Event = new(CrossChainCrossChainPackage) 208 | if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { 209 | it.fail = err 210 | return false 211 | } 212 | it.Event.Raw = log 213 | return true 214 | 215 | default: 216 | return false 217 | } 218 | } 219 | // Iterator still in progress, wait for either a data or an error event 220 | select { 221 | case log := <-it.logs: 222 | it.Event = new(CrossChainCrossChainPackage) 223 | if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { 224 | it.fail = err 225 | return false 226 | } 227 | it.Event.Raw = log 228 | return true 229 | 230 | case err := <-it.sub.Err(): 231 | it.done = true 232 | it.fail = err 233 | return it.Next() 234 | } 235 | } 236 | 237 | // Error returns any retrieval or parsing error occurred during filtering. 238 | func (it *CrossChainCrossChainPackageIterator) Error() error { 239 | return it.fail 240 | } 241 | 242 | // Close terminates the iteration process, releasing any pending underlying 243 | // resources. 244 | func (it *CrossChainCrossChainPackageIterator) Close() error { 245 | it.sub.Unsubscribe() 246 | return nil 247 | } 248 | 249 | // CrossChainCrossChainPackage represents a CrossChainPackage event raised by the CrossChain contract. 250 | type CrossChainCrossChainPackage struct { 251 | ChainId uint16 252 | OracleSequence uint64 253 | PackageSequence uint64 254 | ChannelId uint8 255 | Payload []byte 256 | Raw types.Log // Blockchain specific contextual infos 257 | } 258 | 259 | // FilterCrossChainPackage is a free log retrieval operation binding the contract event 0x3a6e0fc61675aa2a100bcba0568368bb92bcec91c97673391074f11138f0cffe. 260 | // 261 | // Solidity: event crossChainPackage(uint16 chainId, uint64 indexed oracleSequence, uint64 indexed packageSequence, uint8 indexed channelId, bytes payload) 262 | func (_CrossChain *CrossChainFilterer) FilterCrossChainPackage(opts *bind.FilterOpts, oracleSequence []uint64, packageSequence []uint64, channelId []uint8) (*CrossChainCrossChainPackageIterator, error) { 263 | 264 | var oracleSequenceRule []interface{} 265 | for _, oracleSequenceItem := range oracleSequence { 266 | oracleSequenceRule = append(oracleSequenceRule, oracleSequenceItem) 267 | } 268 | var packageSequenceRule []interface{} 269 | for _, packageSequenceItem := range packageSequence { 270 | packageSequenceRule = append(packageSequenceRule, packageSequenceItem) 271 | } 272 | var channelIdRule []interface{} 273 | for _, channelIdItem := range channelId { 274 | channelIdRule = append(channelIdRule, channelIdItem) 275 | } 276 | 277 | logs, sub, err := _CrossChain.contract.FilterLogs(opts, "crossChainPackage", oracleSequenceRule, packageSequenceRule, channelIdRule) 278 | if err != nil { 279 | return nil, err 280 | } 281 | return &CrossChainCrossChainPackageIterator{contract: _CrossChain.contract, event: "crossChainPackage", logs: logs, sub: sub}, nil 282 | } 283 | 284 | // WatchCrossChainPackage is a free log subscription operation binding the contract event 0x3a6e0fc61675aa2a100bcba0568368bb92bcec91c97673391074f11138f0cffe. 285 | // 286 | // Solidity: event crossChainPackage(uint16 chainId, uint64 indexed oracleSequence, uint64 indexed packageSequence, uint8 indexed channelId, bytes payload) 287 | func (_CrossChain *CrossChainFilterer) WatchCrossChainPackage(opts *bind.WatchOpts, sink chan<- *CrossChainCrossChainPackage, oracleSequence []uint64, packageSequence []uint64, channelId []uint8) (event.Subscription, error) { 288 | 289 | var oracleSequenceRule []interface{} 290 | for _, oracleSequenceItem := range oracleSequence { 291 | oracleSequenceRule = append(oracleSequenceRule, oracleSequenceItem) 292 | } 293 | var packageSequenceRule []interface{} 294 | for _, packageSequenceItem := range packageSequence { 295 | packageSequenceRule = append(packageSequenceRule, packageSequenceItem) 296 | } 297 | var channelIdRule []interface{} 298 | for _, channelIdItem := range channelId { 299 | channelIdRule = append(channelIdRule, channelIdItem) 300 | } 301 | 302 | logs, sub, err := _CrossChain.contract.WatchLogs(opts, "crossChainPackage", oracleSequenceRule, packageSequenceRule, channelIdRule) 303 | if err != nil { 304 | return nil, err 305 | } 306 | return event.NewSubscription(func(quit <-chan struct{}) error { 307 | defer sub.Unsubscribe() 308 | for { 309 | select { 310 | case log := <-logs: 311 | // New log arrived, parse the event and forward to the user 312 | event := new(CrossChainCrossChainPackage) 313 | if err := _CrossChain.contract.UnpackLog(event, "crossChainPackage", log); err != nil { 314 | return err 315 | } 316 | event.Raw = log 317 | 318 | select { 319 | case sink <- event: 320 | case err := <-sub.Err(): 321 | return err 322 | case <-quit: 323 | return nil 324 | } 325 | case err := <-sub.Err(): 326 | return err 327 | case <-quit: 328 | return nil 329 | } 330 | } 331 | }), nil 332 | } 333 | 334 | // ParseCrossChainPackage is a log parse operation binding the contract event 0x3a6e0fc61675aa2a100bcba0568368bb92bcec91c97673391074f11138f0cffe. 335 | // 336 | // Solidity: event crossChainPackage(uint16 chainId, uint64 indexed oracleSequence, uint64 indexed packageSequence, uint8 indexed channelId, bytes payload) 337 | func (_CrossChain *CrossChainFilterer) ParseCrossChainPackage(log types.Log) (*CrossChainCrossChainPackage, error) { 338 | event := new(CrossChainCrossChainPackage) 339 | if err := _CrossChain.contract.UnpackLog(event, "crossChainPackage", log); err != nil { 340 | return nil, err 341 | } 342 | event.Raw = log 343 | return event, nil 344 | } 345 | -------------------------------------------------------------------------------- /executor/bsc/abi/CrossChain.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.8; 2 | 3 | contract CrossChain { 4 | event crossChainPackage(uint16 chainId, uint64 indexed oracleSequence, uint64 indexed packageSequence, uint8 indexed channelId, bytes payload); 5 | } -------------------------------------------------------------------------------- /executor/bsc/executor.go: -------------------------------------------------------------------------------- 1 | package bsc 2 | 3 | import ( 4 | "context" 5 | "math/big" 6 | "math/rand" 7 | "strings" 8 | "time" 9 | 10 | "github.com/ethereum/go-ethereum" 11 | "github.com/ethereum/go-ethereum/accounts/abi" 12 | ethcmm "github.com/ethereum/go-ethereum/common" 13 | "github.com/ethereum/go-ethereum/core/types" 14 | "github.com/ethereum/go-ethereum/ethclient" 15 | 16 | "github.com/binance-chain/oracle-relayer/common" 17 | abi2 "github.com/binance-chain/oracle-relayer/executor/bsc/abi" 18 | "github.com/binance-chain/oracle-relayer/util" 19 | ) 20 | 21 | type Executor struct { 22 | Config *util.Config 23 | 24 | CrossChainAbi abi.ABI 25 | Clients []*ethclient.Client 26 | 27 | crossChainContractAddress ethcmm.Address 28 | } 29 | 30 | // NewExecutor returns the bsc executor instance 31 | func NewExecutor(providers []string, config *util.Config) *Executor { 32 | crossChainAbi, err := abi.JSON(strings.NewReader(abi2.CrossChainABI)) 33 | if err != nil { 34 | panic("marshal abi error") 35 | } 36 | 37 | clients := initClients(providers) 38 | 39 | return &Executor{ 40 | Config: config, 41 | CrossChainAbi: crossChainAbi, 42 | Clients: clients, 43 | 44 | crossChainContractAddress: config.ChainConfig.BSCCrossChainContractAddress, 45 | } 46 | } 47 | 48 | func initClients(providers []string) []*ethclient.Client { 49 | clients := make([]*ethclient.Client, 0) 50 | 51 | for _, provider := range providers { 52 | client, err := ethclient.Dial(provider) 53 | if err != nil { 54 | panic("new eth client error") 55 | } 56 | clients = append(clients, client) 57 | } 58 | 59 | return clients 60 | } 61 | 62 | func (e *Executor) getClient() *ethclient.Client { 63 | r := rand.New(rand.NewSource(time.Now().UnixNano())) 64 | 65 | idx := r.Intn(len(e.Clients)) 66 | return e.Clients[idx] 67 | } 68 | 69 | // GetBlockAndPackages returns the block and cross-chain packages of the given height 70 | func (e *Executor) GetBlockAndPackages(height int64) (*common.BlockAndPackageLogs, error) { 71 | ctxWithTimeout, cancel := context.WithTimeout(context.Background(), 5*time.Second) 72 | defer cancel() 73 | 74 | client := e.getClient() 75 | header, err := client.HeaderByNumber(ctxWithTimeout, big.NewInt(height)) 76 | if err != nil { 77 | return nil, err 78 | } 79 | 80 | packageLogs, err := e.GetLogs(client, header) 81 | if err != nil { 82 | return nil, err 83 | } 84 | 85 | return &common.BlockAndPackageLogs{ 86 | Height: height, 87 | BlockHash: header.Hash().String(), 88 | ParentBlockHash: header.ParentHash.String(), 89 | BlockTime: int64(header.Time), 90 | Packages: packageLogs, 91 | }, nil 92 | } 93 | 94 | // GetLogs return the cross-chain packages of the given height 95 | func (e *Executor) GetLogs(client *ethclient.Client, header *types.Header) ([]interface{}, error) { 96 | topics := [][]ethcmm.Hash{{CrossChainPackageEventHash}} 97 | 98 | blockHash := header.Hash() 99 | 100 | ctxWithTimeout, cancel := context.WithTimeout(context.Background(), 5*time.Second) 101 | defer cancel() 102 | 103 | logs, err := client.FilterLogs(ctxWithTimeout, ethereum.FilterQuery{ 104 | BlockHash: &blockHash, 105 | Topics: topics, 106 | Addresses: []ethcmm.Address{e.crossChainContractAddress}, 107 | }) 108 | if err != nil { 109 | return nil, err 110 | } 111 | 112 | packageModels := make([]interface{}, 0, len(logs)) 113 | 114 | for _, log := range logs { 115 | util.Logger.Infof("get log: %d, %s, %s", log.BlockNumber, log.Topics[0].String(), log.TxHash.String()) 116 | 117 | event, err := ParseCrossChainPackageEvent(&e.CrossChainAbi, &log) 118 | if err != nil { 119 | util.Logger.Errorf("parse event log error, er=%s", err.Error()) 120 | continue 121 | } 122 | 123 | if event == nil { 124 | continue 125 | } 126 | 127 | packageModel := event.ToTxLog(&log) 128 | packageModels = append(packageModels, packageModel) 129 | } 130 | return packageModels, nil 131 | } 132 | -------------------------------------------------------------------------------- /executor/bsc/types.go: -------------------------------------------------------------------------------- 1 | package bsc 2 | 3 | import ( 4 | "encoding/hex" 5 | "math/big" 6 | 7 | "github.com/ethereum/go-ethereum/accounts/abi" 8 | "github.com/ethereum/go-ethereum/common" 9 | "github.com/ethereum/go-ethereum/core/types" 10 | 11 | "github.com/binance-chain/oracle-relayer/model" 12 | ) 13 | 14 | var ( 15 | CrossChainPackageEventName = "crossChainPackage" 16 | 17 | CrossChainPackageEventHash = common.HexToHash("0x3a6e0fc61675aa2a100bcba0568368bb92bcec91c97673391074f11138f0cffe") 18 | ) 19 | 20 | type ContractEvent interface { 21 | ToTxLog(log *types.Log) interface{} 22 | } 23 | 24 | type CrossChainPackageEvent struct { 25 | ChainId uint16 26 | OracleSequence uint64 27 | PackageSequence uint64 28 | ChannelId uint8 29 | Payload []byte 30 | } 31 | 32 | func (ev *CrossChainPackageEvent) ToTxLog(log *types.Log) interface{} { 33 | pack := &model.CrossChainPackageLog{ 34 | ChainId: ev.ChainId, 35 | OracleSequence: ev.OracleSequence, 36 | PackageSequence: ev.PackageSequence, 37 | ChannelId: ev.ChannelId, 38 | PayLoad: hex.EncodeToString(ev.Payload), 39 | BlockHash: log.BlockHash.Hex(), 40 | TxHash: log.TxHash.String(), 41 | TxIndex: log.TxIndex, 42 | Height: int64(log.BlockNumber), 43 | } 44 | return pack 45 | } 46 | 47 | func ParseCrossChainPackageEvent(abi *abi.ABI, log *types.Log) (*CrossChainPackageEvent, error) { 48 | var ev CrossChainPackageEvent 49 | 50 | err := abi.UnpackIntoInterface(&ev, CrossChainPackageEventName, log.Data) 51 | if err != nil { 52 | return nil, err 53 | } 54 | 55 | ev.OracleSequence = big.NewInt(0).SetBytes(log.Topics[1].Bytes()).Uint64() 56 | ev.PackageSequence = big.NewInt(0).SetBytes(log.Topics[2].Bytes()).Uint64() 57 | ev.ChannelId = uint8(big.NewInt(0).SetBytes(log.Topics[3].Bytes()).Uint64()) 58 | 59 | return &ev, nil 60 | } 61 | -------------------------------------------------------------------------------- /executor/executor.go: -------------------------------------------------------------------------------- 1 | package executor 2 | 3 | import ( 4 | "github.com/bnb-chain/go-sdk/common/types" 5 | "github.com/bnb-chain/go-sdk/types/msg" 6 | 7 | "github.com/binance-chain/oracle-relayer/common" 8 | ) 9 | 10 | type BbcExecutor interface { 11 | GetAddress() types.ValAddress 12 | GetCurrentSequence(chainId uint16) (int64, error) 13 | GetProphecy(chainId uint16, sequence int64) (*msg.Prophecy, error) 14 | 15 | Claim(chainId uint16, sequence uint64, payload []byte) (string, error) 16 | } 17 | 18 | type BscExecutor interface { 19 | GetBlockAndPackages(height int64) (*common.BlockAndPackageLogs, error) 20 | } 21 | -------------------------------------------------------------------------------- /executor/mock/mock_executor.go: -------------------------------------------------------------------------------- 1 | // Code generated by MockGen. DO NOT EDIT. 2 | // Source: executor/executor.go 3 | 4 | // Package mock is a generated GoMock package. 5 | package mock 6 | 7 | import ( 8 | reflect "reflect" 9 | 10 | types "github.com/bnb-chain/go-sdk/common/types" 11 | msg "github.com/bnb-chain/go-sdk/types/msg" 12 | common "github.com/binance-chain/oracle-relayer/common" 13 | gomock "github.com/golang/mock/gomock" 14 | ) 15 | 16 | // MockBbcExecutor is a mock of BbcExecutor interface 17 | type MockBbcExecutor struct { 18 | ctrl *gomock.Controller 19 | recorder *MockBbcExecutorMockRecorder 20 | } 21 | 22 | // MockBbcExecutorMockRecorder is the mock recorder for MockBbcExecutor 23 | type MockBbcExecutorMockRecorder struct { 24 | mock *MockBbcExecutor 25 | } 26 | 27 | // NewMockBbcExecutor creates a new mock instance 28 | func NewMockBbcExecutor(ctrl *gomock.Controller) *MockBbcExecutor { 29 | mock := &MockBbcExecutor{ctrl: ctrl} 30 | mock.recorder = &MockBbcExecutorMockRecorder{mock} 31 | return mock 32 | } 33 | 34 | // EXPECT returns an object that allows the caller to indicate expected use 35 | func (m *MockBbcExecutor) EXPECT() *MockBbcExecutorMockRecorder { 36 | return m.recorder 37 | } 38 | 39 | // GetAddress mocks base method 40 | func (m *MockBbcExecutor) GetAddress() types.ValAddress { 41 | m.ctrl.T.Helper() 42 | ret := m.ctrl.Call(m, "GetAddress") 43 | ret0, _ := ret[0].(types.ValAddress) 44 | return ret0 45 | } 46 | 47 | // GetAddress indicates an expected call of GetAddress 48 | func (mr *MockBbcExecutorMockRecorder) GetAddress() *gomock.Call { 49 | mr.mock.ctrl.T.Helper() 50 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAddress", reflect.TypeOf((*MockBbcExecutor)(nil).GetAddress)) 51 | } 52 | 53 | // GetCurrentSequence mocks base method 54 | func (m *MockBbcExecutor) GetCurrentSequence(chainId uint16) (int64, error) { 55 | m.ctrl.T.Helper() 56 | ret := m.ctrl.Call(m, "GetCurrentSequence", chainId) 57 | ret0, _ := ret[0].(int64) 58 | ret1, _ := ret[1].(error) 59 | return ret0, ret1 60 | } 61 | 62 | // GetCurrentSequence indicates an expected call of GetCurrentSequence 63 | func (mr *MockBbcExecutorMockRecorder) GetCurrentSequence(chainId interface{}) *gomock.Call { 64 | mr.mock.ctrl.T.Helper() 65 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentSequence", reflect.TypeOf((*MockBbcExecutor)(nil).GetCurrentSequence), chainId) 66 | } 67 | 68 | // GetProphecy mocks base method 69 | func (m *MockBbcExecutor) GetProphecy(chainId uint16, sequence int64) (*msg.Prophecy, error) { 70 | m.ctrl.T.Helper() 71 | ret := m.ctrl.Call(m, "GetProphecy", chainId, sequence) 72 | ret0, _ := ret[0].(*msg.Prophecy) 73 | ret1, _ := ret[1].(error) 74 | return ret0, ret1 75 | } 76 | 77 | // GetProphecy indicates an expected call of GetProphecy 78 | func (mr *MockBbcExecutorMockRecorder) GetProphecy(chainId, sequence interface{}) *gomock.Call { 79 | mr.mock.ctrl.T.Helper() 80 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetProphecy", reflect.TypeOf((*MockBbcExecutor)(nil).GetProphecy), chainId, sequence) 81 | } 82 | 83 | // Claim mocks base method 84 | func (m *MockBbcExecutor) Claim(chainId uint16, sequence uint64, payload []byte) (string, error) { 85 | m.ctrl.T.Helper() 86 | ret := m.ctrl.Call(m, "Claim", chainId, sequence, payload) 87 | ret0, _ := ret[0].(string) 88 | ret1, _ := ret[1].(error) 89 | return ret0, ret1 90 | } 91 | 92 | // Claim indicates an expected call of Claim 93 | func (mr *MockBbcExecutorMockRecorder) Claim(chainId, sequence, payload interface{}) *gomock.Call { 94 | mr.mock.ctrl.T.Helper() 95 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Claim", reflect.TypeOf((*MockBbcExecutor)(nil).Claim), chainId, sequence, payload) 96 | } 97 | 98 | // MockBscExecutor is a mock of BscExecutor interface 99 | type MockBscExecutor struct { 100 | ctrl *gomock.Controller 101 | recorder *MockBscExecutorMockRecorder 102 | } 103 | 104 | // MockBscExecutorMockRecorder is the mock recorder for MockBscExecutor 105 | type MockBscExecutorMockRecorder struct { 106 | mock *MockBscExecutor 107 | } 108 | 109 | // NewMockBscExecutor creates a new mock instance 110 | func NewMockBscExecutor(ctrl *gomock.Controller) *MockBscExecutor { 111 | mock := &MockBscExecutor{ctrl: ctrl} 112 | mock.recorder = &MockBscExecutorMockRecorder{mock} 113 | return mock 114 | } 115 | 116 | // EXPECT returns an object that allows the caller to indicate expected use 117 | func (m *MockBscExecutor) EXPECT() *MockBscExecutorMockRecorder { 118 | return m.recorder 119 | } 120 | 121 | // GetBlockAndPackages mocks base method 122 | func (m *MockBscExecutor) GetBlockAndPackages(height int64) (*common.BlockAndPackageLogs, error) { 123 | m.ctrl.T.Helper() 124 | ret := m.ctrl.Call(m, "GetBlockAndPackages", height) 125 | ret0, _ := ret[0].(*common.BlockAndPackageLogs) 126 | ret1, _ := ret[1].(error) 127 | return ret0, ret1 128 | } 129 | 130 | // GetBlockAndPackages indicates an expected call of GetBlockAndPackages 131 | func (mr *MockBscExecutorMockRecorder) GetBlockAndPackages(height interface{}) *gomock.Call { 132 | mr.mock.ctrl.T.Helper() 133 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBlockAndPackages", reflect.TypeOf((*MockBscExecutor)(nil).GetBlockAndPackages), height) 134 | } 135 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/binance-chain/oracle-relayer 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/PagerDuty/go-pagerduty v1.3.0 7 | github.com/aws/aws-sdk-go v1.25.48 8 | github.com/bnb-chain/go-sdk v1.3.2 9 | github.com/ethereum/go-ethereum v1.10.22 10 | github.com/golang/mock v1.4.4 11 | github.com/gorilla/mux v1.8.0 12 | github.com/jinzhu/gorm v1.9.12 13 | github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 14 | github.com/spf13/pflag v1.0.3 15 | github.com/spf13/viper v1.4.0 16 | github.com/stretchr/testify v1.8.1 17 | github.com/tendermint/tendermint v0.35.9 18 | gopkg.in/natefinch/lumberjack.v2 v2.0.0 19 | ) 20 | 21 | require ( 22 | github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect 23 | github.com/bartekn/go-bip39 v0.0.0-20171116152956-a05967ea095d // indirect 24 | github.com/beorn7/perks v1.0.1 // indirect 25 | github.com/bgentry/speakeasy v0.1.0 // indirect 26 | github.com/bnb-chain/ics23 v0.1.0 // indirect 27 | github.com/bnb-chain/node v0.10.7 // indirect 28 | github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect 29 | github.com/btcsuite/btcd/btcutil v1.1.3 // indirect 30 | github.com/cespare/xxhash/v2 v2.1.2 // indirect 31 | github.com/cosmos/cosmos-sdk v0.25.0 // indirect 32 | github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d // indirect 33 | github.com/cosmos/ledger-go v0.9.2 // indirect 34 | github.com/davecgh/go-spew v1.1.1 // indirect 35 | github.com/deckarep/golang-set v1.8.0 // indirect 36 | github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect 37 | github.com/etcd-io/bbolt v1.3.3 // indirect 38 | github.com/fsnotify/fsnotify v1.4.9 // indirect 39 | github.com/go-kit/kit v0.9.0 // indirect 40 | github.com/go-logfmt/logfmt v0.5.1 // indirect 41 | github.com/go-ole/go-ole v1.2.1 // indirect 42 | github.com/go-sql-driver/mysql v1.4.1 // indirect 43 | github.com/go-stack/stack v1.8.0 // indirect 44 | github.com/gogo/protobuf v1.3.2 // indirect 45 | github.com/golang/protobuf v1.5.2 // indirect 46 | github.com/golang/snappy v0.0.4 // indirect 47 | github.com/google/btree v1.0.0 // indirect 48 | github.com/google/go-querystring v1.0.0 // indirect 49 | github.com/google/uuid v1.2.0 // indirect 50 | github.com/gorilla/websocket v1.5.0 // indirect 51 | github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect 52 | github.com/hashicorp/hcl v1.0.0 // indirect 53 | github.com/inconshreveable/mousetrap v1.0.0 // indirect 54 | github.com/jinzhu/inflection v1.0.0 // indirect 55 | github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af // indirect 56 | github.com/jmhodges/levigo v1.0.0 // indirect 57 | github.com/libp2p/go-buffer-pool v0.0.2 // indirect 58 | github.com/magiconair/properties v1.8.1 // indirect 59 | github.com/mattn/go-isatty v0.0.12 // indirect 60 | github.com/mattn/go-sqlite3 v2.0.1+incompatible // indirect 61 | github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect 62 | github.com/mitchellh/go-homedir v1.1.0 // indirect 63 | github.com/mitchellh/mapstructure v1.4.1 // indirect 64 | github.com/pelletier/go-toml v1.4.0 // indirect 65 | github.com/pkg/errors v0.9.1 // indirect 66 | github.com/pmezard/go-difflib v1.0.0 // indirect 67 | github.com/prometheus/client_golang v1.14.0 // indirect 68 | github.com/prometheus/client_model v0.3.0 // indirect 69 | github.com/prometheus/common v0.37.0 // indirect 70 | github.com/prometheus/procfs v0.8.0 // indirect 71 | github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563 // indirect 72 | github.com/rjeczalik/notify v0.9.1 // indirect 73 | github.com/rs/cors v1.7.0 // indirect 74 | github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect 75 | github.com/spf13/afero v1.2.2 // indirect 76 | github.com/spf13/cast v1.3.0 // indirect 77 | github.com/spf13/cobra v0.0.5 // indirect 78 | github.com/spf13/jwalterweatherman v1.1.0 // indirect 79 | github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect 80 | github.com/tendermint/btcd v0.1.1 // indirect 81 | github.com/tendermint/go-amino v0.15.0 // indirect 82 | github.com/tendermint/iavl v0.12.4 // indirect 83 | github.com/tklauser/go-sysconf v0.3.5 // indirect 84 | github.com/tklauser/numcpus v0.2.2 // indirect 85 | github.com/zondax/hid v0.9.0 // indirect 86 | github.com/zondax/ledger-cosmos-go v0.9.9 // indirect 87 | golang.org/x/crypto v0.5.0 // indirect 88 | golang.org/x/net v0.7.0 // indirect 89 | golang.org/x/sys v0.5.0 // indirect 90 | golang.org/x/text v0.7.0 // indirect 91 | google.golang.org/appengine v1.6.6 // indirect 92 | google.golang.org/genproto v0.0.0-20200825200019-8632dd797987 // indirect 93 | google.golang.org/grpc v1.31.0 // indirect 94 | google.golang.org/protobuf v1.28.1 // indirect 95 | gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect 96 | gopkg.in/yaml.v2 v2.4.0 // indirect 97 | gopkg.in/yaml.v3 v3.0.1 // indirect 98 | ) 99 | 100 | replace ( 101 | github.com/cosmos/cosmos-sdk => github.com/bnb-chain/bnc-cosmos-sdk v0.26.3 102 | github.com/tendermint/go-amino => github.com/bnb-chain/bnc-go-amino v0.14.1-binance.2 103 | github.com/tendermint/iavl => github.com/bnb-chain/bnc-tendermint-iavl v0.12.0-binance.5 104 | github.com/tendermint/tendermint => github.com/bnb-chain/bnc-tendermint v0.32.3-bc.10 105 | github.com/zondax/hid => github.com/binance-chain/hid v0.9.1-0.20190807012304-e1ffd6f0a3cc 106 | github.com/zondax/ledger-cosmos-go => github.com/bnb-chain/ledger-cosmos-go v0.9.10-0.20230201065744-d644bede1667 107 | github.com/zondax/ledger-go => github.com/bnb-chain/ledger-go v0.9.1 108 | golang.org/x/crypto => github.com/tendermint/crypto v0.0.0-20190823183015-45b1026d81ae 109 | ) 110 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 4 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= 5 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 6 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= 7 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= 8 | cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= 9 | cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= 10 | cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= 11 | cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= 12 | cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= 13 | cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= 14 | cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= 15 | cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= 16 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 17 | cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= 18 | cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= 19 | cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= 20 | cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= 21 | cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= 22 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 23 | cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= 24 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 25 | cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= 26 | cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= 27 | cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= 28 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 29 | cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= 30 | cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= 31 | cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= 32 | cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= 33 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 34 | github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= 35 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 36 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 37 | github.com/DataDog/zstd v1.3.5 h1:DtpNbljikUepEPD16hD4LvIcmhnhdLTiW/5pHgbmp14= 38 | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 39 | github.com/PagerDuty/go-pagerduty v1.3.0 h1:2vWajLxpmGeP8pmsyZ0MjFneHa8ASJrztJRSe3FNOzg= 40 | github.com/PagerDuty/go-pagerduty v1.3.0/go.mod h1:W5hSIIPrzSgAkNBDiuymWN5g9yQVzimL7BUBL44f3RY= 41 | github.com/Shopify/sarama v1.21.0 h1:0GKs+e8mn1RRUzfg9oUXv3v7ZieQLmOZF/bfnmmGhM8= 42 | github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8= 43 | github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= 44 | github.com/VictoriaMetrics/fastcache v1.6.0 h1:C/3Oi3EiBCqufydp1neRZkqcwmEiuRT9c3fqvvgKm5o= 45 | github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= 46 | github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= 47 | github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= 48 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 49 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 50 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 51 | github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 52 | github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= 53 | github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= 54 | github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= 55 | github.com/aws/aws-sdk-go v1.25.48 h1:J82DYDGZHOKHdhx6hD24Tm30c2C3GchYGfN0mf9iKUk= 56 | github.com/aws/aws-sdk-go v1.25.48/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= 57 | github.com/bartekn/go-bip39 v0.0.0-20171116152956-a05967ea095d h1:1aAija9gr0Hyv4KfQcRcwlmFIrhkDmIj2dz5bkg/s/8= 58 | github.com/bartekn/go-bip39 v0.0.0-20171116152956-a05967ea095d/go.mod h1:icNx/6QdFblhsEjZehARqbNumymUT/ydwlLojFdv7Sk= 59 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 60 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 61 | github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 62 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 63 | github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= 64 | github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= 65 | github.com/binance-chain/hid v0.9.1-0.20190807012304-e1ffd6f0a3cc h1:9+QIrE2KiZea71rSsXFoNSvRH/WB1Bb8unb9q8D+gCs= 66 | github.com/binance-chain/hid v0.9.1-0.20190807012304-e1ffd6f0a3cc/go.mod h1:70xsYy/oIYCEXIDRoXZD9A9DfipsxVbEi6CUa+gAjo0= 67 | github.com/bnb-chain/bnc-cosmos-sdk v0.26.3 h1:D/hi2wttArF0NZsO+uOjs6kIKHcLjpEuFv2QrxjsQqQ= 68 | github.com/bnb-chain/bnc-cosmos-sdk v0.26.3/go.mod h1:XiDYVT+XqECR+AyCBO4KBsrbL/d1x2UTsVU36SvHxI8= 69 | github.com/bnb-chain/bnc-go-amino v0.14.1-binance.2 h1:iAlp9gqG0f2LGAauf3ZiijWlT6NI+W2r9y70HH9LI3k= 70 | github.com/bnb-chain/bnc-go-amino v0.14.1-binance.2/go.mod h1:LiCO7jev+3HwLGAiN9gpD0z+jTz95RqgSavbse55XOY= 71 | github.com/bnb-chain/bnc-tendermint v0.32.3-bc.10 h1:E4iSwEbJCLYchHiHE1gnOM3jjmJXLBxARhy/RCl8CpI= 72 | github.com/bnb-chain/bnc-tendermint v0.32.3-bc.10/go.mod h1:wKxpgQYxtZxPasF59zca7NbmIazOjUdualm1gMEMGTU= 73 | github.com/bnb-chain/bnc-tendermint-iavl v0.12.0-binance.5 h1:8trIShwwXvCUQz34DwIsgPz2yJgVlZRMv+2IwEuKjTM= 74 | github.com/bnb-chain/bnc-tendermint-iavl v0.12.0-binance.5/go.mod h1:4Kf0qoRDM4vc1ZTBSHqwiQyVniCPS+HAAY434oYzr6w= 75 | github.com/bnb-chain/go-sdk v1.3.2 h1:g6vDwOIZJSlObfwOOeCJ4UCKUIG7rPdap40ZW85Y0BU= 76 | github.com/bnb-chain/go-sdk v1.3.2/go.mod h1:2MxV6FZx8mOhZPmP3Wm/omRstxPlolGnmwP9GUXESbg= 77 | github.com/bnb-chain/ics23 v0.0.0-20221021082321-d0a365dd9898/go.mod h1:cU6lTGolbbLFsGCgceNB2AzplH1xecLp6+KXvxM32nI= 78 | github.com/bnb-chain/ics23 v0.1.0 h1:DvjGOts2FBfbxB48384CYD1LbcrfjThFz8kowY/7KxU= 79 | github.com/bnb-chain/ics23 v0.1.0/go.mod h1:cU6lTGolbbLFsGCgceNB2AzplH1xecLp6+KXvxM32nI= 80 | github.com/bnb-chain/ledger-cosmos-go v0.9.10-0.20230201065744-d644bede1667 h1:0/dBO1McxuSjea9T/4mf7wcTgEgTs2GR2Cz4qktl+/o= 81 | github.com/bnb-chain/ledger-cosmos-go v0.9.10-0.20230201065744-d644bede1667/go.mod h1:NnMWSpPmLxWfVsPoTVlpmZ3g2uHi9N99qe2gxGv0uwQ= 82 | github.com/bnb-chain/node v0.10.7 h1:cQqpsyq3C2LwcG1eh12i9Gnn7Rd0Tupk07nUcuImxNg= 83 | github.com/bnb-chain/node v0.10.7/go.mod h1:wLUCM8A8wP8bsorQfuPu6hBQQVeULZvWjaxv9TMF9/c= 84 | github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= 85 | github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= 86 | github.com/btcsuite/btcd v0.23.0 h1:V2/ZgjfDFIygAX3ZapeigkVBoVUtOJKSwrhZdlpSvaA= 87 | github.com/btcsuite/btcd v0.23.0/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY= 88 | github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= 89 | github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= 90 | github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= 91 | github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= 92 | github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= 93 | github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= 94 | github.com/btcsuite/btcd/btcutil v1.1.3 h1:xfbtw8lwpp0G6NwSHb+UE67ryTFHJAiNuipusjXSohQ= 95 | github.com/btcsuite/btcd/btcutil v1.1.3/go.mod h1:UR7dsSJzJUfMmFiiLlIrMq1lS9jh9EdCV7FStZSnpi0= 96 | github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= 97 | github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= 98 | github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= 99 | github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= 100 | github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= 101 | github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= 102 | github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= 103 | github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= 104 | github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= 105 | github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= 106 | github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= 107 | github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= 108 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 109 | github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= 110 | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 111 | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 112 | github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= 113 | github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 114 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 115 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 116 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 117 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 118 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 119 | github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= 120 | github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 121 | github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= 122 | github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 123 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 124 | github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= 125 | github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d h1:49RLWk1j44Xu4fjHb6JFYmeUnDORVwHNkDxaQ0ctCVU= 126 | github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= 127 | github.com/cosmos/ledger-go v0.9.2 h1:Nnao/dLwaVTk1Q5U9THldpUMMXU94BOTWPddSmVB6pI= 128 | github.com/cosmos/ledger-go v0.9.2/go.mod h1:oZJ2hHAZROdlHiwTg4t7kP+GKIIkBT+o6c9QWFanOyI= 129 | github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= 130 | github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= 131 | github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= 132 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 133 | github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 134 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 135 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 136 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 137 | github.com/deathowl/go-metrics-prometheus v0.0.0-20200518174047-74482eab5bfb h1:vhS5LxjRvIc6ptzKtTjzYV2rEsuAvTtez27UJkKIrjc= 138 | github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4= 139 | github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo= 140 | github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= 141 | github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= 142 | github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= 143 | github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= 144 | github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= 145 | github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM= 146 | github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= 147 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 148 | github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= 149 | github.com/eapache/go-resiliency v1.1.0 h1:1NtRmCAqadE2FN4ZcN6g90TP3uk8cg9rn9eNK2197aU= 150 | github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 h1:YEetp8/yCZMuEPMUDHG0CW/brkkEp8mzqk2+ODEitlw= 151 | github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= 152 | github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= 153 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 154 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 155 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 156 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 157 | github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y= 158 | github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= 159 | github.com/etcd-io/bbolt v1.3.3 h1:gSJmxrs37LgTqR/oyJBWok6k6SvXEUerFTbltIhXkBM= 160 | github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= 161 | github.com/ethereum/go-ethereum v1.10.22 h1:HbEgsDo1YTGIf4KB/NNpn+XH+PiNJXUZ9ksRxiqWyMc= 162 | github.com/ethereum/go-ethereum v1.10.22/go.mod h1:EYFyF19u3ezGLD4RqOkLq+ZCXzYbLoNDdZlMt7kyKFg= 163 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 164 | github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= 165 | github.com/fortytw2/leaktest v1.2.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= 166 | github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= 167 | github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= 168 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 169 | github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= 170 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 171 | github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI= 172 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 173 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 174 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 175 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 176 | github.com/go-kit/kit v0.6.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 177 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 178 | github.com/go-kit/kit v0.9.0 h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk= 179 | github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 180 | github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= 181 | github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= 182 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 183 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 184 | github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= 185 | github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= 186 | github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= 187 | github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E= 188 | github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= 189 | github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= 190 | github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= 191 | github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= 192 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 193 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 194 | github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= 195 | github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= 196 | github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= 197 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 198 | github.com/golang-jwt/jwt/v4 v4.3.0 h1:kHL1vqdqWNfATmA0FNMdmZNMyZI1U6O31X4rlIPoBog= 199 | github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= 200 | github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= 201 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 202 | github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 203 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 204 | github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 205 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 206 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 207 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 208 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 209 | github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 210 | github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 211 | github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 212 | github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= 213 | github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= 214 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 215 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 216 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 217 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 218 | github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 219 | github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= 220 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 221 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 222 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 223 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 224 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 225 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 226 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 227 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 228 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 229 | github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= 230 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 231 | github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 232 | github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= 233 | github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 234 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 235 | github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= 236 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 237 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 238 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 239 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 240 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 241 | github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 242 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 243 | github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 244 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 245 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 246 | github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= 247 | github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 248 | github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= 249 | github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= 250 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 251 | github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa h1:Q75Upo5UN4JbPFURXZ8nLKYUvF85dyFRop/vQ0Rv+64= 252 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 253 | github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 254 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 255 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 256 | github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 257 | github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 258 | github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 259 | github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 260 | github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 261 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 262 | github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= 263 | github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 264 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 265 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 266 | github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= 267 | github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= 268 | github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= 269 | github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 270 | github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= 271 | github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 272 | github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= 273 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= 274 | github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= 275 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 276 | github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= 277 | github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= 278 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 279 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 280 | github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs= 281 | github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= 282 | github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= 283 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 284 | github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= 285 | github.com/holiman/uint256 v1.2.0 h1:gpSYcPLWGv4sG43I2mVLiDZCNDh/EpGjSk8tmtxitHM= 286 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 287 | github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ= 288 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 289 | github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= 290 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 291 | github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= 292 | github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= 293 | github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= 294 | github.com/jinzhu/gorm v1.9.12 h1:Drgk1clyWT9t9ERbzHza6Mj/8FY/CqMyVzOiHviMo6Q= 295 | github.com/jinzhu/gorm v1.9.12/go.mod h1:vhTjlKSJUTWNtcbQtrMBFCxy7eXTzeCAzfL5fBZT/Qs= 296 | github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= 297 | github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= 298 | github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M= 299 | github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 300 | github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= 301 | github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= 302 | github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U= 303 | github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= 304 | github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 305 | github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= 306 | github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= 307 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 308 | github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 309 | github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 310 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 311 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 312 | github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= 313 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 314 | github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= 315 | github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 316 | github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= 317 | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 318 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 319 | github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= 320 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 321 | github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 322 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 323 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 324 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 325 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 326 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 327 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 328 | github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4= 329 | github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 330 | github.com/libp2p/go-buffer-pool v0.0.2 h1:QNK2iAFa8gjAe1SPz6mHSMuCcjs+X1wlHzeOSqcmlfs= 331 | github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM= 332 | github.com/linkedin/goavro v0.0.0-20180427201934-fa8f6a30176c h1:5VZFnez+6M7nwhbIT8L4DtViRFR8GpYpZj97EVAphuk= 333 | github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 334 | github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= 335 | github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 336 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 337 | github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= 338 | github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 339 | github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= 340 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 341 | github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= 342 | github.com/mattn/go-sqlite3 v2.0.1+incompatible h1:xQ15muvnzGBHpIpdrNi1DA5x0+TcBZzsIDwmw9uTHzw= 343 | github.com/mattn/go-sqlite3 v2.0.1+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= 344 | github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= 345 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 346 | github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= 347 | github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= 348 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 349 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 350 | github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= 351 | github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 352 | github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= 353 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 354 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 355 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 356 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 357 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 358 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 359 | github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 360 | github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM= 361 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= 362 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 363 | github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= 364 | github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= 365 | github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= 366 | github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= 367 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 368 | github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 369 | github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= 370 | github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= 371 | github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= 372 | github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= 373 | github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 374 | github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= 375 | github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= 376 | github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= 377 | github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88= 378 | github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= 379 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 380 | github.com/pelletier/go-toml v1.4.0 h1:u3Z1r+oOXJIkxqw34zVhyPgjBsm6X2wn21NWs/HfSeg= 381 | github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= 382 | github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I= 383 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 384 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 385 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 386 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 387 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 388 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 389 | github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= 390 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 391 | github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= 392 | github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= 393 | github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= 394 | github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= 395 | github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= 396 | github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= 397 | github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= 398 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 399 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 400 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 401 | github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 402 | github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= 403 | github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= 404 | github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= 405 | github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 406 | github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 407 | github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= 408 | github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= 409 | github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= 410 | github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= 411 | github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= 412 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 413 | github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 414 | github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 415 | github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= 416 | github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= 417 | github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= 418 | github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= 419 | github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= 420 | github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA= 421 | github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= 422 | github.com/rcrowley/go-metrics v0.0.0-20180503174638-e2704e165165/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= 423 | github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563 h1:dY6ETXrvDG7Sa4vE8ZQG4yqWg6UnOcbqTAahkV813vQ= 424 | github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= 425 | github.com/rjeczalik/notify v0.9.1 h1:CLCKso/QK1snAlnhNR/CNvNiFU2saUtjV0bx3EwNeCE= 426 | github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho= 427 | github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= 428 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 429 | github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= 430 | github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= 431 | github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= 432 | github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= 433 | github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= 434 | github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= 435 | github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= 436 | github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= 437 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 438 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 439 | github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= 440 | github.com/snikch/goodman v0.0.0-20171125024755-10e37e294daa/go.mod h1:oJyF+mSPHbB5mVY2iO9KV3pTt/QbIkGaO8gQ2WrDbP4= 441 | github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= 442 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 443 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= 444 | github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= 445 | github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= 446 | github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= 447 | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 448 | github.com/spf13/cobra v0.0.1/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= 449 | github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= 450 | github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= 451 | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= 452 | github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= 453 | github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= 454 | github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= 455 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 456 | github.com/spf13/viper v1.0.0/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM= 457 | github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= 458 | github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU= 459 | github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= 460 | github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4 h1:Gb2Tyox57NRNuZ2d3rmvB3pcmbu7O1RS3m8WRx7ilrg= 461 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 462 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 463 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 464 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 465 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 466 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 467 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 468 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 469 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 470 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 471 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 472 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 473 | github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM= 474 | github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= 475 | github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= 476 | github.com/tendermint/btcd v0.1.1 h1:0VcxPfflS2zZ3RiOAHkBiFUcPvbtRj5O7zHmcJWHV7s= 477 | github.com/tendermint/btcd v0.1.1/go.mod h1:DC6/m53jtQzr/NFmMNEu0rxf18/ktVoVtMrnDD5pN+U= 478 | github.com/tendermint/crypto v0.0.0-20190823183015-45b1026d81ae h1:AOXNM7c2Vvo45SjAgeWF8Wy+NS7/NCqzRNpUc+HPAec= 479 | github.com/tendermint/crypto v0.0.0-20190823183015-45b1026d81ae/go.mod h1:z4YtwM70uOnk8h0pjJYlj3zdYwi9l03By6iAIF5j/Pk= 480 | github.com/tklauser/go-sysconf v0.3.5 h1:uu3Xl4nkLzQfXNsWn15rPc/HQCJKObbt1dKJeWp3vU4= 481 | github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= 482 | github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA= 483 | github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM= 484 | github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= 485 | github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef h1:wHSqTBrZW24CsNJDfeh9Ex6Pm0Rcpc7qrgKBiL44vF4= 486 | github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= 487 | github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= 488 | github.com/urfave/cli/v2 v2.10.2 h1:x3p8awjp/2arX+Nl/G2040AZpOCHS/eMJJ1/a+mye4Y= 489 | github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= 490 | github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= 491 | github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= 492 | github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 493 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 494 | github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 495 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 496 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 497 | go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= 498 | go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk= 499 | go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= 500 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 501 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 502 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 503 | go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 504 | go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 505 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 506 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 507 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 508 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 509 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 510 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 511 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 512 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 513 | golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 514 | golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 515 | golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 516 | golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= 517 | golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= 518 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 519 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 520 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 521 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 522 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 523 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 524 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 525 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 526 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 527 | golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= 528 | golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 529 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 530 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 531 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 532 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 533 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 534 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 535 | golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 536 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 537 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 538 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 539 | golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 540 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 541 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 542 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 543 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 544 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 545 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 546 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 547 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 548 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 549 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 550 | golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 551 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 552 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 553 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 554 | golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 555 | golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 556 | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 557 | golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 558 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 559 | golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 560 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 561 | golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 562 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 563 | golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 564 | golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 565 | golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 566 | golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 567 | golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 568 | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 569 | golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 570 | golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 571 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 572 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 573 | golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 574 | golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= 575 | golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= 576 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 577 | golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= 578 | golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= 579 | golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 580 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 581 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 582 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 583 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 584 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 585 | golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 586 | golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= 587 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 588 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 589 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 590 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 591 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 592 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 593 | golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 594 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 595 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 596 | golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 597 | golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 598 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw= 599 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 600 | golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 601 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 602 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 603 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 604 | golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 605 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 606 | golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 607 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 608 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 609 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 610 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 611 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 612 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 613 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 614 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 615 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 616 | golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 617 | golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 618 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 619 | golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 620 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 621 | golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 622 | golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 623 | golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 624 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 625 | golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 626 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 627 | golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 628 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 629 | golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 630 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 631 | golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 632 | golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 633 | golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 634 | golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 635 | golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 636 | golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 637 | golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 638 | golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 639 | golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 640 | golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 641 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 642 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 643 | golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 644 | golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 645 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 646 | golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 647 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 648 | golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 649 | golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 650 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 651 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 652 | golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 653 | golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 654 | golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= 655 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 656 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 657 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 658 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 659 | golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 660 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 661 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 662 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 663 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 664 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 665 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 666 | golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= 667 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 668 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 669 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 670 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 671 | golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE= 672 | golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 673 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 674 | golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 675 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 676 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 677 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 678 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 679 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 680 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 681 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 682 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 683 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 684 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 685 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 686 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 687 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 688 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 689 | golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 690 | golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 691 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 692 | golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 693 | golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 694 | golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 695 | golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 696 | golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 697 | golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 698 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 699 | golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 700 | golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 701 | golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 702 | golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 703 | golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 704 | golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 705 | golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 706 | golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= 707 | golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 708 | golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 709 | golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 710 | golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 711 | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 712 | golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 713 | golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 714 | golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 715 | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 716 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 717 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 718 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 719 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 720 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 721 | golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df h1:5Pf6pFKu98ODmgnpvkJ3kFUOQGGLIzLIkbzUHp47618= 722 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 723 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 724 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 725 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 726 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 727 | google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 728 | google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 729 | google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 730 | google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 731 | google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 732 | google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 733 | google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 734 | google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 735 | google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 736 | google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= 737 | google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= 738 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 739 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 740 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 741 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 742 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 743 | google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= 744 | google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 745 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 746 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 747 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 748 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 749 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 750 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 751 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 752 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 753 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 754 | google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 755 | google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 756 | google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 757 | google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 758 | google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 759 | google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= 760 | google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 761 | google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 762 | google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 763 | google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 764 | google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 765 | google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 766 | google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 767 | google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 768 | google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= 769 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 770 | google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= 771 | google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 772 | google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 773 | google.golang.org/genproto v0.0.0-20200825200019-8632dd797987 h1:PDIOdWxZ8eRizhKa1AAvY53xsvLB1cWorMjslvY3VA8= 774 | google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 775 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 776 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 777 | google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 778 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 779 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 780 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 781 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 782 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 783 | google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 784 | google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= 785 | google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= 786 | google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 787 | google.golang.org/grpc v1.31.0 h1:T7P4R73V3SSDPhH7WW7ATbfViLtmamH0DKrP3f9AuDI= 788 | google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 789 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 790 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 791 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 792 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 793 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 794 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 795 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 796 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 797 | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= 798 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 799 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 800 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 801 | google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= 802 | google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 803 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 804 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 805 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 806 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 807 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= 808 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 809 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 810 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 811 | gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= 812 | gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= 813 | gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= 814 | gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= 815 | gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= 816 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= 817 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 818 | gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 819 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 820 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 821 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 822 | gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 823 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 824 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 825 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 826 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 827 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 828 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 829 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 830 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 831 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 832 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 833 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 834 | honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 835 | honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 836 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 837 | rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= 838 | rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= 839 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | 7 | "github.com/bnb-chain/go-sdk/common/types" 8 | "github.com/jinzhu/gorm" 9 | _ "github.com/jinzhu/gorm/dialects/mysql" 10 | _ "github.com/jinzhu/gorm/dialects/sqlite" 11 | "github.com/spf13/pflag" 12 | "github.com/spf13/viper" 13 | 14 | "github.com/binance-chain/oracle-relayer/admin" 15 | "github.com/binance-chain/oracle-relayer/executor/bbc" 16 | "github.com/binance-chain/oracle-relayer/executor/bsc" 17 | "github.com/binance-chain/oracle-relayer/model" 18 | "github.com/binance-chain/oracle-relayer/observer" 19 | "github.com/binance-chain/oracle-relayer/relayer" 20 | "github.com/binance-chain/oracle-relayer/util" 21 | ) 22 | 23 | const ( 24 | flagConfigType = "config-type" 25 | flagConfigAwsRegion = "aws-region" 26 | flagConfigAwsSecretKey = "aws-secret-key" 27 | flagConfigPath = "config-path" 28 | flagBBCNetwork = "bbc-network" 29 | ) 30 | 31 | const ( 32 | ConfigTypeLocal = "local" 33 | ConfigTypeAws = "aws" 34 | ) 35 | 36 | func initFlags() { 37 | flag.String(flagConfigPath, "", "config path") 38 | flag.String(flagConfigType, "", "config type, local or aws") 39 | flag.String(flagConfigAwsRegion, "", "aws s3 region") 40 | flag.String(flagConfigAwsSecretKey, "", "aws s3 secret key") 41 | flag.Int(flagBBCNetwork, int(types.TestNetwork), "bbc chain network type") 42 | 43 | pflag.CommandLine.AddGoFlagSet(flag.CommandLine) 44 | pflag.Parse() 45 | err := viper.BindPFlags(pflag.CommandLine) 46 | if err != nil { 47 | panic(fmt.Sprintf("bind flags error, err=%s", err)) 48 | } 49 | } 50 | 51 | func printUsage() { 52 | fmt.Print("usage: ./relayer --bbc-network [0 for testnet, 1 for mainnet] --config-type [local or aws] --config-path config_file_path\n") 53 | } 54 | 55 | func main() { 56 | initFlags() 57 | 58 | bbcNetwork := viper.GetInt(flagBBCNetwork) 59 | if bbcNetwork != int(types.TestNetwork) && 60 | bbcNetwork != int(types.ProdNetwork) && 61 | bbcNetwork != int(types.TmpTestNetwork) && 62 | bbcNetwork != int(types.GangesNetwork) { 63 | printUsage() 64 | return 65 | } 66 | 67 | types.SetNetwork(types.ChainNetwork(bbcNetwork)) 68 | 69 | configType := viper.GetString(flagConfigType) 70 | if configType == "" { 71 | printUsage() 72 | return 73 | } 74 | 75 | if configType != ConfigTypeAws && configType != ConfigTypeLocal { 76 | printUsage() 77 | return 78 | } 79 | 80 | var config *util.Config 81 | if configType == ConfigTypeAws { 82 | awsSecretKey := viper.GetString(flagConfigAwsSecretKey) 83 | if awsSecretKey == "" { 84 | printUsage() 85 | return 86 | } 87 | 88 | awsRegion := viper.GetString(flagConfigAwsRegion) 89 | if awsRegion == "" { 90 | printUsage() 91 | return 92 | } 93 | 94 | configContent, err := util.GetSecret(awsSecretKey, awsRegion) 95 | if err != nil { 96 | fmt.Printf("get aws config error, err=%s", err.Error()) 97 | return 98 | } 99 | config = util.ParseConfigFromJson(configContent) 100 | } else { 101 | configFilePath := viper.GetString(flagConfigPath) 102 | if configFilePath == "" { 103 | printUsage() 104 | return 105 | } 106 | config = util.ParseConfigFromFile(configFilePath) 107 | } 108 | config.Validate() 109 | 110 | // init logger 111 | util.InitLogger(*config.LogConfig) 112 | util.InitAlert(config.AlertConfig) 113 | 114 | db, err := gorm.Open(config.DBConfig.Dialect, config.DBConfig.DBPath) 115 | if err != nil { 116 | panic(fmt.Sprintf("open db error, err=%s", err.Error())) 117 | } 118 | defer db.Close() 119 | model.InitTables(db) 120 | 121 | bscExecutor := bsc.NewExecutor(config.ChainConfig.BSCProviders, config) 122 | ob := observer.NewObserver(db, config, bscExecutor) 123 | go ob.Start() 124 | 125 | bbcExecutor, err := bbc.NewExecutor(config.ChainConfig.BBCRpcAddrs, types.ChainNetwork(bbcNetwork), config) 126 | if err != nil { 127 | fmt.Printf("new bbc executor error, err=%s\n", err.Error()) 128 | return 129 | } 130 | oracleRelayer := relayer.NewRelayer(db, bbcExecutor, config) 131 | go oracleRelayer.Main() 132 | 133 | adm := admin.NewAdmin(config, bbcExecutor) 134 | go adm.Serve() 135 | 136 | select {} 137 | } 138 | -------------------------------------------------------------------------------- /model/model.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/jinzhu/gorm" 7 | ) 8 | 9 | type BlockLog struct { 10 | Id int64 11 | Chain string 12 | BlockHash string 13 | ParentHash string 14 | Height int64 15 | BlockTime int64 16 | CreateTime int64 17 | } 18 | 19 | func (BlockLog) TableName() string { 20 | return "block_log" 21 | } 22 | 23 | func (l *BlockLog) BeforeCreate() (err error) { 24 | l.CreateTime = time.Now().Unix() 25 | return nil 26 | } 27 | 28 | type PackageStatus int 29 | 30 | const ( 31 | PackageStatusInit PackageStatus = 0 32 | PackageStatusConfirmed PackageStatus = 1 33 | PackageStatusClaimed PackageStatus = 2 34 | ) 35 | 36 | type CrossChainPackageLog struct { 37 | Id int64 38 | ChainId uint16 39 | OracleSequence uint64 40 | PackageSequence uint64 41 | ChannelId uint8 42 | PayLoad string `gorm:"type:text"` 43 | TxIndex uint 44 | 45 | Status PackageStatus 46 | BlockHash string 47 | TxHash string 48 | ClaimTxHash string 49 | Height int64 50 | ConfirmedNum int64 51 | CreateTime int64 52 | UpdateTime int64 53 | } 54 | 55 | func (l *CrossChainPackageLog) BeforeCreate() (err error) { 56 | l.CreateTime = time.Now().Unix() 57 | l.UpdateTime = time.Now().Unix() 58 | return nil 59 | } 60 | 61 | func (CrossChainPackageLog) TableName() string { 62 | return "cross_chain_package_log" 63 | } 64 | 65 | func InitTables(db *gorm.DB) { 66 | if !db.HasTable(&BlockLog{}) { 67 | db.CreateTable(&BlockLog{}) 68 | db.Model(&BlockLog{}).AddUniqueIndex("idx_block_log_height", "height") 69 | db.Model(&BlockLog{}).AddIndex("idx_block_log_create_time", "create_time") 70 | } 71 | 72 | if !db.HasTable(&CrossChainPackageLog{}) { 73 | db.CreateTable(&CrossChainPackageLog{}) 74 | db.Model(&CrossChainPackageLog{}).AddIndex("idx_package_log_channel_seq", "channel_id", "oracle_sequence") 75 | db.Model(&CrossChainPackageLog{}).AddIndex("idx_package_log_height", "height") 76 | db.Model(&CrossChainPackageLog{}).AddIndex("idx_package_log_status", "status") 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /observer/observer.go: -------------------------------------------------------------------------------- 1 | package observer 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/jinzhu/gorm" 8 | 9 | "github.com/binance-chain/oracle-relayer/common" 10 | "github.com/binance-chain/oracle-relayer/executor" 11 | "github.com/binance-chain/oracle-relayer/model" 12 | "github.com/binance-chain/oracle-relayer/util" 13 | ) 14 | 15 | type Observer struct { 16 | DB *gorm.DB 17 | Config *util.Config 18 | BscExecutor executor.BscExecutor 19 | } 20 | 21 | // NewObserver returns the observer instance 22 | func NewObserver(db *gorm.DB, cfg *util.Config, bscExecutor executor.BscExecutor) *Observer { 23 | return &Observer{ 24 | DB: db, 25 | Config: cfg, 26 | BscExecutor: bscExecutor, 27 | } 28 | } 29 | 30 | // Start starts the routines of observer 31 | func (ob *Observer) Start() { 32 | go ob.Fetch(ob.Config.ChainConfig.BSCStartHeight) 33 | go ob.Prune() 34 | go ob.Alert() 35 | } 36 | 37 | // Fetch starts the main routine for fetching blocks of BSC 38 | func (ob *Observer) Fetch(startHeight int64) { 39 | for { 40 | curBlockLog, err := ob.GetCurrentBlockLog() 41 | if err != nil { 42 | util.Logger.Errorf("get current block log error, err=%s", err.Error()) 43 | time.Sleep(common.ObserverFetchInterval) 44 | continue 45 | } 46 | 47 | nextHeight := curBlockLog.Height + 1 48 | if curBlockLog.Height == 0 && startHeight != 0 { 49 | nextHeight = startHeight 50 | } 51 | 52 | util.Logger.Infof("fetch block, height=%d", nextHeight) 53 | err = ob.fetchBlock(curBlockLog.Height, nextHeight, curBlockLog.BlockHash) 54 | if err != nil { 55 | util.Logger.Errorf("fetch block error, err=%s", err.Error()) 56 | time.Sleep(common.ObserverFetchInterval) 57 | } 58 | } 59 | } 60 | 61 | // fetchBlock fetches the next block of BSC and saves it to database. if the next block hash 62 | // does not match to the parent hash, the current block will be deleted for there is a fork. 63 | func (ob *Observer) fetchBlock(curHeight, nextHeight int64, curBlockHash string) error { 64 | blockAndPackageLogs, err := ob.BscExecutor.GetBlockAndPackages(nextHeight) 65 | if err != nil { 66 | return fmt.Errorf("get block info error, height=%d, err=%s", nextHeight, err.Error()) 67 | } 68 | 69 | parentHash := blockAndPackageLogs.ParentBlockHash 70 | if curHeight != 0 && parentHash != curBlockHash { 71 | return ob.DeleteBlockAndPackages(curHeight) 72 | } else { 73 | nextBlockLog := model.BlockLog{ 74 | BlockHash: blockAndPackageLogs.BlockHash, 75 | ParentHash: parentHash, 76 | Height: blockAndPackageLogs.Height, 77 | BlockTime: blockAndPackageLogs.BlockTime, 78 | } 79 | 80 | err := ob.SaveBlockAndPackages(&nextBlockLog, blockAndPackageLogs.Packages) 81 | if err != nil { 82 | return err 83 | } 84 | 85 | err = ob.UpdateConfirmedNum(nextBlockLog.Height) 86 | if err != nil { 87 | return err 88 | } 89 | } 90 | return nil 91 | } 92 | 93 | // DeleteBlockAndPackages deletes the block and txs of the given height 94 | func (ob *Observer) DeleteBlockAndPackages(height int64) error { 95 | tx := ob.DB.Begin() 96 | if err := tx.Error; err != nil { 97 | return err 98 | } 99 | 100 | if err := tx.Where("height = ?", height).Delete(model.BlockLog{}).Error; err != nil { 101 | tx.Rollback() 102 | return err 103 | } 104 | 105 | if err := tx.Where("height = ? and status = ?", height, model.PackageStatusInit).Delete(model.CrossChainPackageLog{}).Error; err != nil { 106 | tx.Rollback() 107 | return err 108 | } 109 | 110 | return tx.Commit().Error 111 | } 112 | 113 | // UpdateConfirmedNum updates confirmation number of cross-chain packages. 114 | func (ob *Observer) UpdateConfirmedNum(height int64) error { 115 | err := ob.DB.Model(model.CrossChainPackageLog{}).Where("status = ?", model.PackageStatusInit).Updates( 116 | map[string]interface{}{ 117 | "confirmed_num": gorm.Expr("? - height", height+1), 118 | "update_time": time.Now().Unix(), 119 | }).Error 120 | if err != nil { 121 | return err 122 | } 123 | 124 | err = ob.DB.Model(model.CrossChainPackageLog{}).Where("status = ? and confirmed_num >= ?", 125 | model.PackageStatusInit, ob.Config.ChainConfig.BSCConfirmNum).Updates( 126 | map[string]interface{}{ 127 | "status": model.PackageStatusConfirmed, 128 | "update_time": time.Now().Unix(), 129 | }).Error 130 | if err != nil { 131 | return err 132 | } 133 | 134 | return nil 135 | } 136 | 137 | // Prune prunes the outdated blocks 138 | func (ob *Observer) Prune() { 139 | for { 140 | curBlockLog, err := ob.GetCurrentBlockLog() 141 | if err != nil { 142 | util.Logger.Errorf("get current block log error, err=%s", err.Error()) 143 | time.Sleep(common.ObserverPruneInterval) 144 | 145 | continue 146 | } 147 | err = ob.DB.Where("height < ?", curBlockLog.Height-common.ObserverMaxBlockNumber).Delete(model.BlockLog{}).Error 148 | if err != nil { 149 | util.Logger.Infof("prune block logs error, err=%s", err.Error()) 150 | } 151 | time.Sleep(common.ObserverPruneInterval) 152 | } 153 | } 154 | 155 | // SaveBlockAndPackages saves block and packages to database 156 | func (ob *Observer) SaveBlockAndPackages(blockLog *model.BlockLog, packages []interface{}) error { 157 | tx := ob.DB.Begin() 158 | if err := tx.Error; err != nil { 159 | return err 160 | } 161 | 162 | if err := tx.Create(blockLog).Error; err != nil { 163 | tx.Rollback() 164 | return err 165 | } 166 | 167 | for _, pack := range packages { 168 | if err := tx.Create(pack).Error; err != nil { 169 | tx.Rollback() 170 | return err 171 | } 172 | } 173 | return tx.Commit().Error 174 | } 175 | 176 | // GetCurrentBlockLog returns the highest block log 177 | func (ob *Observer) GetCurrentBlockLog() (*model.BlockLog, error) { 178 | blockLog := model.BlockLog{} 179 | err := ob.DB.Order("height desc").First(&blockLog).Error 180 | if err != nil && err != gorm.ErrRecordNotFound { 181 | return nil, err 182 | } 183 | return &blockLog, nil 184 | } 185 | 186 | // Alert sends alerts to tg group if there is no new block fetched in a specific time 187 | func (ob *Observer) Alert() { 188 | for { 189 | curOtherChainBlockLog, err := ob.GetCurrentBlockLog() 190 | if err != nil { 191 | util.Logger.Errorf("get current block log error, err=%s", err.Error()) 192 | time.Sleep(common.ObserverAlertInterval) 193 | 194 | continue 195 | } 196 | if curOtherChainBlockLog.Height > 0 { 197 | if time.Now().Unix()-curOtherChainBlockLog.CreateTime > ob.Config.AlertConfig.BlockUpdateTimeOut { 198 | msg := fmt.Sprintf("[%s] last smart chain block fetched at %s, height=%d", 199 | ob.Config.AlertConfig.Moniker, time.Unix(curOtherChainBlockLog.CreateTime, 0).String(), curOtherChainBlockLog.Height) 200 | util.SendTelegramMessage(msg) 201 | util.SendPagerDutyAlert(msg, util.IncidentDedupKeyBlockTimeout) 202 | } 203 | } 204 | 205 | time.Sleep(common.ObserverAlertInterval) 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /observer/observer_test.go: -------------------------------------------------------------------------------- 1 | package observer 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | 7 | "github.com/golang/mock/gomock" 8 | "github.com/jinzhu/gorm" 9 | _ "github.com/jinzhu/gorm/dialects/sqlite" 10 | "github.com/stretchr/testify/require" 11 | 12 | "github.com/binance-chain/oracle-relayer/common" 13 | "github.com/binance-chain/oracle-relayer/executor/mock" 14 | "github.com/binance-chain/oracle-relayer/model" 15 | "github.com/binance-chain/oracle-relayer/util" 16 | ) 17 | 18 | func TestObserver_fetchBlock_error(t *testing.T) { 19 | ctrl := gomock.NewController(t) 20 | defer ctrl.Finish() 21 | 22 | config := util.GetTestConfig() 23 | db, err := util.PrepareDB(config) 24 | require.Nil(t, err, "create db error") 25 | 26 | bscExecutor := mock.NewMockBscExecutor(ctrl) 27 | bscExecutor.EXPECT().GetBlockAndPackages(gomock.Any()).AnyTimes().Return(nil, errors.New("error")) 28 | 29 | ob := NewObserver(db, config, bscExecutor) 30 | err = ob.fetchBlock(1, 2, "1") 31 | require.NotNil(t, err, "error should not be nil") 32 | 33 | require.Contains(t, err.Error(), "get block info error") 34 | } 35 | 36 | func TestObserver_fetchBlock_error_wrongParentHash(t *testing.T) { 37 | ctrl := gomock.NewController(t) 38 | defer ctrl.Finish() 39 | 40 | config := util.GetTestConfig() 41 | db, err := util.PrepareDB(config) 42 | require.Nil(t, err, "create db error") 43 | 44 | bscExecutor := mock.NewMockBscExecutor(ctrl) 45 | bscExecutor.EXPECT().GetBlockAndPackages(gomock.Any()).AnyTimes().Return( 46 | &common.BlockAndPackageLogs{ 47 | Height: 3, 48 | BlockHash: "3", 49 | ParentBlockHash: "2_1", 50 | }, nil) 51 | 52 | ob := NewObserver(db, config, bscExecutor) 53 | 54 | blockLog1 := &model.BlockLog{ 55 | Height: 1, 56 | BlockHash: "1", 57 | ParentHash: "0", 58 | BlockTime: 0, 59 | } 60 | db.Create(blockLog1) 61 | 62 | blockLog2 := &model.BlockLog{ 63 | Height: 2, 64 | BlockHash: "2", 65 | ParentHash: "1", 66 | BlockTime: 0, 67 | } 68 | db.Create(blockLog2) 69 | 70 | packageLog2 := &model.CrossChainPackageLog{ 71 | ChainId: 96, 72 | OracleSequence: 1, 73 | PackageSequence: 1, 74 | ChannelId: 2, 75 | Height: 2, 76 | TxHash: "tx_hash", 77 | } 78 | db.Create(packageLog2) 79 | 80 | err = ob.fetchBlock(2, 3, "2") 81 | require.Nil(t, err, "error should be nil") 82 | 83 | deletedBlockLog := &model.BlockLog{} 84 | err = db.Where("height = ?", blockLog2.Height).First(&deletedBlockLog).Error 85 | 86 | require.Equal(t, err, gorm.ErrRecordNotFound, "error should be ErrRecordNotFound") 87 | 88 | deletedPackage := &model.CrossChainPackageLog{} 89 | err = db.Where("height = ?", packageLog2.Height).First(&deletedPackage).Error 90 | require.Equal(t, err, gorm.ErrRecordNotFound, "error should be ErrRecordNotFound") 91 | } 92 | 93 | func TestObserver_fetchBlock_error_rightParentHash(t *testing.T) { 94 | ctrl := gomock.NewController(t) 95 | defer ctrl.Finish() 96 | 97 | config := util.GetTestConfig() 98 | db, err := util.PrepareDB(config) 99 | require.Nil(t, err, "create db error") 100 | 101 | bscExecutor := mock.NewMockBscExecutor(ctrl) 102 | bscExecutor.EXPECT().GetBlockAndPackages(gomock.Any()).AnyTimes().Return( 103 | &common.BlockAndPackageLogs{ 104 | Height: 3, 105 | BlockHash: "3", 106 | ParentBlockHash: "2", 107 | Packages: []interface{}{ 108 | &model.CrossChainPackageLog{ 109 | ChainId: 96, 110 | OracleSequence: 2, 111 | PackageSequence: 2, 112 | ChannelId: 2, 113 | Height: 3, 114 | TxHash: "tx_hash_1", 115 | }, 116 | &model.CrossChainPackageLog{ 117 | ChainId: 96, 118 | OracleSequence: 2, 119 | PackageSequence: 3, 120 | ChannelId: 2, 121 | Height: 3, 122 | TxHash: "tx_hash_2", 123 | }, 124 | }, 125 | }, nil) 126 | 127 | ob := NewObserver(db, config, bscExecutor) 128 | 129 | blockLog1 := &model.BlockLog{ 130 | Height: 1, 131 | BlockHash: "1", 132 | ParentHash: "0", 133 | BlockTime: 0, 134 | } 135 | db.Create(blockLog1) 136 | 137 | blockLog2 := &model.BlockLog{ 138 | Height: 2, 139 | BlockHash: "2", 140 | ParentHash: "1", 141 | BlockTime: 0, 142 | } 143 | db.Create(blockLog2) 144 | 145 | err = ob.fetchBlock(2, 3, "2") 146 | require.Nil(t, err, "error should be nil") 147 | 148 | newBlockLog := &model.BlockLog{} 149 | err = db.Where("height = ?", 3).First(&newBlockLog).Error 150 | 151 | require.Nil(t, err, "error should be nil") 152 | require.Equal(t, newBlockLog.Height, int64(3), "height should be 3") 153 | 154 | newPackages := make([]*model.CrossChainPackageLog, 0) 155 | err = db.Where("height = ?", 3).Find(&newPackages).Error 156 | 157 | require.Nil(t, err, "error should be nil") 158 | require.Equal(t, len(newPackages), 2, "length of packages should be 2") 159 | } 160 | -------------------------------------------------------------------------------- /relayer/relayer.go: -------------------------------------------------------------------------------- 1 | package relayer 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "time" 7 | 8 | "github.com/bnb-chain/go-sdk/common/types" 9 | "github.com/bnb-chain/go-sdk/types/msg" 10 | "github.com/ethereum/go-ethereum/rlp" 11 | "github.com/jinzhu/gorm" 12 | 13 | "github.com/binance-chain/oracle-relayer/common" 14 | "github.com/binance-chain/oracle-relayer/executor" 15 | "github.com/binance-chain/oracle-relayer/model" 16 | "github.com/binance-chain/oracle-relayer/util" 17 | ) 18 | 19 | type Relayer struct { 20 | DB *gorm.DB 21 | BBCExecutor executor.BbcExecutor 22 | Config *util.Config 23 | } 24 | 25 | // NewRelayer returns the relayer instance 26 | func NewRelayer(db *gorm.DB, bbcExecutor executor.BbcExecutor, cfg *util.Config) *Relayer { 27 | return &Relayer{ 28 | DB: db, 29 | BBCExecutor: bbcExecutor, 30 | Config: cfg, 31 | } 32 | } 33 | 34 | // Main starts the routines of relayer 35 | func (r *Relayer) Main() { 36 | go r.RelayPackages() 37 | 38 | go r.Alert() 39 | } 40 | 41 | // RelayPackages starts the main routine for processing the cross-chain packages 42 | func (r *Relayer) RelayPackages() { 43 | for { 44 | err := r.process(r.Config.ChainConfig.BSCChainId) 45 | if err != nil { 46 | time.Sleep(time.Duration(r.Config.ChainConfig.RelayInterval) * time.Millisecond) 47 | } 48 | } 49 | } 50 | 51 | // process relays the next batch of packages to BNB Beacon Chain 52 | func (r *Relayer) process(chainId uint16) error { 53 | sequence, err := r.BBCExecutor.GetCurrentSequence(chainId) 54 | if err != nil { 55 | util.Logger.Errorf("get current sequence error: chainId=%d, err=%s", 56 | chainId, err.Error()) 57 | return err 58 | } 59 | 60 | util.Logger.Infof("current sequence, chain_id=%d, seq=%d", chainId, sequence) 61 | 62 | claimLogs := make([]*model.CrossChainPackageLog, 0) 63 | err = r.DB.Where("oracle_sequence = ? and chain_id = ? and status = ?", 64 | sequence, chainId, model.PackageStatusConfirmed).Order("tx_index asc").Find(&claimLogs).Error 65 | if err != nil { 66 | util.Logger.Errorf("query claim log error: err=%s", err.Error()) 67 | return err 68 | } 69 | 70 | if len(claimLogs) == 0 { 71 | return fmt.Errorf("no packages found") 72 | } 73 | 74 | prophecy, err := r.BBCExecutor.GetProphecy(chainId, sequence) 75 | if err != nil { 76 | util.Logger.Errorf("get prophecy error: err=%s", err.Error()) 77 | return err 78 | } 79 | 80 | validatorAddress := r.BBCExecutor.GetAddress() 81 | if prophecy != nil && prophecy.ValidatorClaims != nil && prophecy.ValidatorClaims[validatorAddress.String()] != "" { 82 | return fmt.Errorf("already claimed") 83 | } 84 | 85 | packages := make(msg.Packages, 0, len(claimLogs)) 86 | for _, claimLog := range claimLogs { 87 | payload, err := hex.DecodeString(claimLog.PayLoad) 88 | if err != nil { 89 | return fmt.Errorf("decode payload error, payload=%s", claimLog.PayLoad) 90 | } 91 | 92 | pack := msg.Package{ 93 | ChannelId: types.IbcChannelID(claimLog.ChannelId), 94 | Sequence: claimLog.PackageSequence, 95 | Payload: payload, 96 | } 97 | packages = append(packages, pack) 98 | } 99 | 100 | encodedPackages, err := rlp.EncodeToBytes(packages) 101 | if err != nil { 102 | return fmt.Errorf("encode packages error, err=%s", err.Error()) 103 | } 104 | 105 | util.Logger.Infof("claim, chain_id=%d, seq=%d, payload=%s", 106 | chainId, sequence, hex.EncodeToString(encodedPackages)) 107 | txHash, err := r.BBCExecutor.Claim(chainId, uint64(sequence), encodedPackages) 108 | if err != nil { 109 | util.Logger.Errorf("claim error: err=%s", err.Error()) 110 | return err 111 | } 112 | 113 | err = r.DB.Model(model.CrossChainPackageLog{}).Where("oracle_sequence = ? and chain_id = ?", sequence, chainId).Update(map[string]interface{}{ 114 | "status": model.PackageStatusClaimed, 115 | "claim_tx_hash": txHash, 116 | "update_time": time.Now().Unix(), 117 | }).Error 118 | if err != nil { 119 | util.Logger.Errorf("update CrossChainPackageLog error, err=%s", err.Error()) 120 | } 121 | return err 122 | } 123 | 124 | // Alert sends alert to tg group if there is any package delayed 125 | func (r *Relayer) Alert() { 126 | for { 127 | time.Sleep(common.PackageDelayAlertInterval) 128 | 129 | sequence, err := r.BBCExecutor.GetCurrentSequence(r.Config.ChainConfig.BSCChainId) 130 | if err != nil { 131 | util.Logger.Errorf("get current sequence error: chainId=%d, err=%s", 132 | r.Config.ChainConfig.BSCChainId, err.Error()) 133 | continue 134 | } 135 | 136 | claimLog := &model.CrossChainPackageLog{} 137 | 138 | err = r.DB.Where("chain_id = ? and status = ? and oracle_sequence >= ?", 139 | r.Config.ChainConfig.BSCChainId, model.PackageStatusConfirmed, sequence).Order("oracle_sequence asc").First(&claimLog).Error 140 | if err != nil && err != gorm.ErrRecordNotFound { 141 | util.Logger.Errorf("query claim log error: err=%s", err.Error()) 142 | continue 143 | } 144 | 145 | if claimLog.Id == 0 { 146 | continue 147 | } 148 | 149 | if time.Now().Unix()-claimLog.UpdateTime > r.Config.AlertConfig.PackageDelayAlertThreshold { 150 | alertMsg := fmt.Sprintf("[%s] cross chain package was confirmed but not relayed, confiremd_time=%s, sequence=%d", 151 | r.Config.AlertConfig.Moniker, time.Unix(claimLog.UpdateTime, 0).String(), claimLog.OracleSequence) 152 | 153 | util.SendTelegramMessage(alertMsg) 154 | util.SendPagerDutyAlert(alertMsg, util.IncidentDedupKeyRelayError) 155 | } 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /relayer/relayer_test.go: -------------------------------------------------------------------------------- 1 | package relayer 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | 7 | "github.com/bnb-chain/go-sdk/types/msg" 8 | 9 | "github.com/bnb-chain/go-sdk/common/types" 10 | 11 | "github.com/binance-chain/oracle-relayer/model" 12 | 13 | "github.com/golang/mock/gomock" 14 | _ "github.com/jinzhu/gorm/dialects/sqlite" 15 | "github.com/stretchr/testify/require" 16 | 17 | "github.com/binance-chain/oracle-relayer/executor/mock" 18 | "github.com/binance-chain/oracle-relayer/util" 19 | ) 20 | 21 | func TestRelayer_process_getSequenceError(t *testing.T) { 22 | ctrl := gomock.NewController(t) 23 | defer ctrl.Finish() 24 | 25 | config := util.GetTestConfig() 26 | db, err := util.PrepareDB(config) 27 | require.Nil(t, err, "create db error") 28 | 29 | bbcExecutor := mock.NewMockBbcExecutor(ctrl) 30 | bbcExecutor.EXPECT().GetCurrentSequence(gomock.Any()).AnyTimes().Return(int64(0), errors.New("get sequence error")) 31 | 32 | relayer := NewRelayer(db, bbcExecutor, config) 33 | err = relayer.process(96) 34 | require.NotNil(t, err, "error should not be nil") 35 | 36 | require.Contains(t, err.Error(), "get sequence error") 37 | } 38 | 39 | func TestRelayer_process_emptyLogs(t *testing.T) { 40 | ctrl := gomock.NewController(t) 41 | defer ctrl.Finish() 42 | 43 | config := util.GetTestConfig() 44 | db, err := util.PrepareDB(config) 45 | require.Nil(t, err, "create db error") 46 | 47 | bbcExecutor := mock.NewMockBbcExecutor(ctrl) 48 | bbcExecutor.EXPECT().GetCurrentSequence(gomock.Any()).AnyTimes().Return(int64(1), nil) 49 | 50 | relayer := NewRelayer(db, bbcExecutor, config) 51 | err = relayer.process(96) 52 | require.NotNil(t, err, "error should not be nil") 53 | 54 | require.Contains(t, err.Error(), "no packages found") 55 | } 56 | 57 | func TestRelayer_process_getProphecyError(t *testing.T) { 58 | ctrl := gomock.NewController(t) 59 | defer ctrl.Finish() 60 | 61 | config := util.GetTestConfig() 62 | db, err := util.PrepareDB(config) 63 | require.Nil(t, err, "create db error") 64 | 65 | bbcExecutor := mock.NewMockBbcExecutor(ctrl) 66 | bbcExecutor.EXPECT().GetCurrentSequence(gomock.Any()).AnyTimes().Return(int64(1), nil) 67 | bbcExecutor.EXPECT().GetProphecy(gomock.Any(), gomock.Any()).AnyTimes().Return(nil, errors.New("get prophecy error")) 68 | 69 | relayer := NewRelayer(db, bbcExecutor, config) 70 | 71 | packageLog := &model.CrossChainPackageLog{ 72 | ChainId: 96, 73 | OracleSequence: 1, 74 | PackageSequence: 1, 75 | ChannelId: 2, 76 | Height: 2, 77 | Status: 1, 78 | TxHash: "tx_hash", 79 | } 80 | db.Create(packageLog) 81 | 82 | err = relayer.process(96) 83 | require.NotNil(t, err, "error should not be nil") 84 | 85 | require.Contains(t, err.Error(), "get prophecy error") 86 | } 87 | 88 | func TestRelayer_process_alreadyClaimed(t *testing.T) { 89 | ctrl := gomock.NewController(t) 90 | defer ctrl.Finish() 91 | 92 | config := util.GetTestConfig() 93 | db, err := util.PrepareDB(config) 94 | require.Nil(t, err, "create db error") 95 | 96 | validatorAddr, err := types.AccAddressFromBech32("bnb1w7puzjxu05ktc5zvpnzkndt6tyl720nsutzvpg") 97 | require.Nil(t, err, "error should be nil") 98 | 99 | bbcExecutor := mock.NewMockBbcExecutor(ctrl) 100 | bbcExecutor.EXPECT().GetCurrentSequence(gomock.Any()).AnyTimes().Return(int64(1), nil) 101 | bbcExecutor.EXPECT().GetProphecy(gomock.Any(), gomock.Any()).AnyTimes().Return(&msg.Prophecy{ 102 | ID: "1", 103 | Status: msg.Status{}, 104 | ClaimValidators: nil, 105 | ValidatorClaims: map[string]string{ 106 | types.ValAddress(validatorAddr).String(): "claim", 107 | }, 108 | }, nil) 109 | bbcExecutor.EXPECT().GetAddress().AnyTimes().Return(types.ValAddress(validatorAddr)) 110 | 111 | relayer := NewRelayer(db, bbcExecutor, config) 112 | 113 | packageLog := &model.CrossChainPackageLog{ 114 | ChainId: 96, 115 | OracleSequence: 1, 116 | PackageSequence: 1, 117 | ChannelId: 2, 118 | Height: 2, 119 | Status: 1, 120 | TxHash: "tx_hash", 121 | } 122 | db.Create(packageLog) 123 | 124 | err = relayer.process(96) 125 | require.NotNil(t, err, "error should not be nil") 126 | 127 | require.Contains(t, err.Error(), "already claimed") 128 | } 129 | 130 | func TestRelayer_process_claimError(t *testing.T) { 131 | ctrl := gomock.NewController(t) 132 | defer ctrl.Finish() 133 | 134 | config := util.GetTestConfig() 135 | db, err := util.PrepareDB(config) 136 | require.Nil(t, err, "create db error") 137 | 138 | validatorAddr, err := types.AccAddressFromBech32("bnb1w7puzjxu05ktc5zvpnzkndt6tyl720nsutzvpg") 139 | require.Nil(t, err, "error should be nil") 140 | 141 | bbcExecutor := mock.NewMockBbcExecutor(ctrl) 142 | bbcExecutor.EXPECT().GetCurrentSequence(gomock.Any()).AnyTimes().Return(int64(1), nil) 143 | bbcExecutor.EXPECT().GetProphecy(gomock.Any(), gomock.Any()).AnyTimes().Return(nil, nil) 144 | bbcExecutor.EXPECT().GetAddress().AnyTimes().Return(types.ValAddress(validatorAddr)) 145 | bbcExecutor.EXPECT().Claim(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return("", errors.New("claim error")) 146 | 147 | relayer := NewRelayer(db, bbcExecutor, config) 148 | 149 | packageLog := &model.CrossChainPackageLog{ 150 | ChainId: 96, 151 | OracleSequence: 1, 152 | PackageSequence: 1, 153 | ChannelId: 2, 154 | Height: 2, 155 | Status: 1, 156 | TxHash: "tx_hash", 157 | } 158 | db.Create(packageLog) 159 | 160 | err = relayer.process(96) 161 | require.NotNil(t, err, "error should not be nil") 162 | 163 | require.Contains(t, err.Error(), "claim error") 164 | } 165 | 166 | func TestRelayer_process_claimSuccess(t *testing.T) { 167 | ctrl := gomock.NewController(t) 168 | defer ctrl.Finish() 169 | 170 | config := util.GetTestConfig() 171 | db, err := util.PrepareDB(config) 172 | require.Nil(t, err, "create db error") 173 | 174 | validatorAddr, err := types.AccAddressFromBech32("bnb1w7puzjxu05ktc5zvpnzkndt6tyl720nsutzvpg") 175 | require.Nil(t, err, "error should be nil") 176 | 177 | bbcExecutor := mock.NewMockBbcExecutor(ctrl) 178 | bbcExecutor.EXPECT().GetCurrentSequence(gomock.Any()).AnyTimes().Return(int64(1), nil) 179 | bbcExecutor.EXPECT().GetProphecy(gomock.Any(), gomock.Any()).AnyTimes().Return(nil, nil) 180 | bbcExecutor.EXPECT().GetAddress().AnyTimes().Return(types.ValAddress(validatorAddr)) 181 | bbcExecutor.EXPECT().Claim(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return("tx_hash", nil) 182 | 183 | relayer := NewRelayer(db, bbcExecutor, config) 184 | 185 | packageLog := &model.CrossChainPackageLog{ 186 | ChainId: 96, 187 | OracleSequence: 1, 188 | PackageSequence: 1, 189 | ChannelId: 2, 190 | Height: 2, 191 | Status: 1, 192 | TxHash: "tx_hash", 193 | } 194 | db.Create(packageLog) 195 | 196 | err = relayer.process(96) 197 | require.Nil(t, err, "error should be nil") 198 | 199 | newPackage := &model.CrossChainPackageLog{} 200 | err = db.Where("height = ?", 2).First(newPackage).Error 201 | require.Nil(t, err, "error should be nil") 202 | 203 | require.Equal(t, newPackage.TxHash, "tx_hash") 204 | require.Equal(t, newPackage.Status, model.PackageStatusClaimed) 205 | } 206 | -------------------------------------------------------------------------------- /util/aws.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "encoding/base64" 5 | 6 | "github.com/aws/aws-sdk-go/aws" 7 | "github.com/aws/aws-sdk-go/aws/session" 8 | "github.com/aws/aws-sdk-go/service/secretsmanager" 9 | ) 10 | 11 | // GetSecret retrieves the secret from aws secret manager 12 | func GetSecret(secretName, region string) (string, error) { 13 | //Create a Secrets Manager client 14 | sess, err := session.NewSession(&aws.Config{ 15 | Region: ®ion, 16 | }) 17 | if err != nil { 18 | return "", err 19 | } 20 | 21 | svc := secretsmanager.New(sess) 22 | input := &secretsmanager.GetSecretValueInput{ 23 | SecretId: aws.String(secretName), 24 | VersionStage: aws.String("AWSCURRENT"), // VersionStage defaults to AWSCURRENT if unspecified 25 | } 26 | 27 | result, err := svc.GetSecretValue(input) 28 | if err != nil { 29 | return "", err 30 | } 31 | 32 | var secretString, decodedBinarySecret string 33 | if result.SecretString != nil { 34 | secretString = *result.SecretString 35 | return secretString, nil 36 | } else { 37 | decodedBinarySecretBytes := make([]byte, base64.StdEncoding.DecodedLen(len(result.SecretBinary))) 38 | length, err := base64.StdEncoding.Decode(decodedBinarySecretBytes, result.SecretBinary) 39 | if err != nil { 40 | return "", err 41 | } 42 | decodedBinarySecret = string(decodedBinarySecretBytes[:length]) 43 | return decodedBinarySecret, nil 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /util/config.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | 8 | ethcmm "github.com/ethereum/go-ethereum/common" 9 | 10 | "github.com/binance-chain/oracle-relayer/common" 11 | ) 12 | 13 | const ( 14 | KeyTypeMnemonic = "mnemonic" 15 | KeyTypeAWSMnemonic = "aws_mnemonic" 16 | ) 17 | 18 | type Config struct { 19 | DBConfig *DBConfig `json:"db_config"` 20 | ChainConfig *ChainConfig `json:"chain_config"` 21 | LogConfig *LogConfig `json:"log_config"` 22 | AlertConfig *AlertConfig `json:"alert_config"` 23 | AdminConfig *AdminConfig `json:"admin_config"` 24 | } 25 | 26 | func (cfg *Config) Validate() { 27 | cfg.DBConfig.Validate() 28 | cfg.ChainConfig.Validate() 29 | cfg.LogConfig.Validate() 30 | cfg.AlertConfig.Validate() 31 | } 32 | 33 | type AlertConfig struct { 34 | Moniker string `json:"moniker"` 35 | 36 | TelegramBotId string `json:"telegram_bot_id"` 37 | TelegramChatId string `json:"telegram_chat_id"` 38 | 39 | PagerDutyAuthToken string `json:"pager_duty_auth_token"` 40 | 41 | BlockUpdateTimeOut int64 `json:"block_update_time_out"` 42 | PackageDelayAlertThreshold int64 `json:"package_delay_alert_threshold"` 43 | } 44 | 45 | func (cfg *AlertConfig) Validate() { 46 | if cfg.Moniker == "" { 47 | panic("moniker should not be empty") 48 | } 49 | 50 | if cfg.BlockUpdateTimeOut <= 0 { 51 | panic("block_update_time_out should be larger than 0") 52 | } 53 | 54 | if cfg.PackageDelayAlertThreshold <= 0 { 55 | panic("package_delay_alert_threshold should be larger than 0") 56 | } 57 | } 58 | 59 | type DBConfig struct { 60 | Dialect string `json:"dialect"` 61 | DBPath string `json:"db_path"` 62 | } 63 | 64 | func (cfg *DBConfig) Validate() { 65 | if cfg.Dialect != common.DBDialectMysql && cfg.Dialect != common.DBDialectSqlite3 { 66 | panic(fmt.Sprintf("only %s and %s supported", common.DBDialectMysql, common.DBDialectSqlite3)) 67 | } 68 | if cfg.DBPath == "" { 69 | panic("db path should not be empty") 70 | } 71 | } 72 | 73 | type ChainConfig struct { 74 | BSCStartHeight int64 `json:"bsc_start_height"` 75 | BSCProviders []string `json:"bsc_providers"` 76 | BSCConfirmNum int64 `json:"bsc_confirm_num"` 77 | BSCChainId uint16 `json:"bsc_chain_id"` 78 | BSCCrossChainContractAddress ethcmm.Address `json:"bsc_cross_chain_contract_address"` 79 | 80 | BBCRpcAddrs []string `json:"bbc_rpc_addrs"` 81 | BBCMnemonic string `json:"bbc_mnemonic"` 82 | BBCKeyType string `json:"bbc_key_type"` 83 | BBCAWSRegion string `json:"bbc_aws_region"` 84 | BBCAWSSecretName string `json:"bbc_aws_secret_name"` 85 | 86 | RelayInterval int64 `json:"relay_interval"` 87 | } 88 | 89 | func (cfg *ChainConfig) Validate() { 90 | if cfg.BSCStartHeight < 0 { 91 | panic("bsc_start_height should not be less than 0") 92 | } 93 | if len(cfg.BSCProviders) == 0 { 94 | panic("bsc_providers should not be empty") 95 | } 96 | if cfg.BSCConfirmNum <= 0 { 97 | panic("bsc_confirm_num should be larger than 0") 98 | } 99 | 100 | // replace bsc_confirm_num if it is less than DefaultConfirmNum 101 | if cfg.BSCConfirmNum <= common.DefaultConfirmNum { 102 | cfg.BSCConfirmNum = common.DefaultConfirmNum 103 | } 104 | 105 | var emptyAddr ethcmm.Address 106 | if cfg.BSCCrossChainContractAddress.String() == emptyAddr.String() { 107 | panic("bsc_token_hub_contract_address should not be empty") 108 | } 109 | 110 | if len(cfg.BBCRpcAddrs) == 0 { 111 | panic("bbc_rpc_addrs should not be empty") 112 | } 113 | if cfg.BBCKeyType != KeyTypeMnemonic && cfg.BBCKeyType != KeyTypeAWSMnemonic { 114 | panic(fmt.Sprintf("bbc_key_type of bnb beacon chain chain only supports %s and %s", KeyTypeMnemonic, KeyTypeAWSMnemonic)) 115 | } 116 | if cfg.BBCKeyType == KeyTypeAWSMnemonic && cfg.BBCAWSRegion == "" { 117 | panic("bbc_aws_region of bnb beacon chain chain should not be empty") 118 | } 119 | if cfg.BBCKeyType == KeyTypeAWSMnemonic && cfg.BBCAWSSecretName == "" { 120 | panic("bbc_aws_secret_name of bnb beacon chain chain should not be empty") 121 | } 122 | if cfg.BBCKeyType == KeyTypeMnemonic && cfg.BBCMnemonic == "" { 123 | panic("bbc_mnemonic should not be empty") 124 | } 125 | 126 | if cfg.RelayInterval <= 0 { 127 | panic(fmt.Sprintf("relay interval should be larger than 0")) 128 | } 129 | } 130 | 131 | type LogConfig struct { 132 | Level string `json:"level"` 133 | Filename string `json:"filename"` 134 | MaxFileSizeInMB int `json:"max_file_size_in_mb"` 135 | MaxBackupsOfLogFiles int `json:"max_backups_of_log_files"` 136 | MaxAgeToRetainLogFilesInDays int `json:"max_age_to_retain_log_files_in_days"` 137 | UseConsoleLogger bool `json:"use_console_logger"` 138 | UseFileLogger bool `json:"use_file_logger"` 139 | Compress bool `json:"compress"` 140 | } 141 | 142 | func (cfg *LogConfig) Validate() { 143 | if cfg.UseFileLogger { 144 | if cfg.Filename == "" { 145 | panic("filename should not be empty if use file logger") 146 | } 147 | if cfg.MaxFileSizeInMB <= 0 { 148 | panic("max_file_size_in_mb should be larger than 0 if use file logger") 149 | } 150 | if cfg.MaxBackupsOfLogFiles <= 0 { 151 | panic("max_backups_off_log_files should be larger than 0 if use file logger") 152 | } 153 | } 154 | } 155 | 156 | type AdminConfig struct { 157 | ListenAddr string `json:"listen_addr"` 158 | } 159 | 160 | // ParseConfigFromFile returns the config from json file 161 | func ParseConfigFromFile(filePath string) *Config { 162 | bz, err := ioutil.ReadFile(filePath) 163 | if err != nil { 164 | panic(err) 165 | } 166 | 167 | var config Config 168 | if err := json.Unmarshal(bz, &config); err != nil { 169 | panic(err) 170 | } 171 | return &config 172 | } 173 | 174 | // ParseConfigFromJson returns the config from json string 175 | func ParseConfigFromJson(content string) *Config { 176 | var config Config 177 | if err := json.Unmarshal([]byte(content), &config); err != nil { 178 | panic(err) 179 | } 180 | return &config 181 | } 182 | -------------------------------------------------------------------------------- /util/config_test.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "testing" 5 | 6 | ethcmm "github.com/ethereum/go-ethereum/common" 7 | 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func TestAlertConfig(t *testing.T) { 12 | cases := []struct { 13 | config *AlertConfig 14 | result bool 15 | }{ 16 | { 17 | &AlertConfig{ 18 | Moniker: "", 19 | }, 20 | true, 21 | }, { 22 | &AlertConfig{ 23 | BlockUpdateTimeOut: 0, 24 | }, 25 | true, 26 | }, { 27 | &AlertConfig{ 28 | PackageDelayAlertThreshold: 0, 29 | }, 30 | true, 31 | }, { 32 | &AlertConfig{ 33 | Moniker: "test", 34 | BlockUpdateTimeOut: 10, 35 | PackageDelayAlertThreshold: 10, 36 | }, 37 | false, 38 | }, 39 | } 40 | 41 | for _, config := range cases { 42 | if config.result { 43 | require.Panics(t, config.config.Validate, "the check should panic") 44 | } else { 45 | require.NotPanics(t, config.config.Validate, "the check should not panic") 46 | } 47 | } 48 | } 49 | 50 | func TestDBConfig(t *testing.T) { 51 | cases := []struct { 52 | config *DBConfig 53 | result bool 54 | }{ 55 | { 56 | &DBConfig{ 57 | Dialect: "wrong", 58 | }, 59 | true, 60 | }, { 61 | &DBConfig{ 62 | Dialect: "mysql", 63 | DBPath: "", 64 | }, 65 | true, 66 | }, { 67 | &DBConfig{ 68 | Dialect: "mysql", 69 | DBPath: "path", 70 | }, 71 | false, 72 | }, 73 | } 74 | 75 | for _, config := range cases { 76 | if config.result { 77 | require.Panics(t, config.config.Validate, "the check should panic") 78 | } else { 79 | require.NotPanics(t, config.config.Validate, "the check should not panic") 80 | } 81 | } 82 | } 83 | 84 | func TestChainConfig(t *testing.T) { 85 | cases := []struct { 86 | config *ChainConfig 87 | result bool 88 | }{ 89 | { 90 | &ChainConfig{ 91 | BSCStartHeight: -1, 92 | }, 93 | true, 94 | }, { 95 | &ChainConfig{ 96 | BSCStartHeight: 1, 97 | BSCProviders: []string{}, 98 | }, 99 | true, 100 | }, { 101 | &ChainConfig{ 102 | BSCStartHeight: 1, 103 | BSCProviders: []string{"provider"}, 104 | BSCConfirmNum: 0, 105 | }, 106 | true, 107 | }, { 108 | &ChainConfig{ 109 | BSCStartHeight: 1, 110 | BSCProviders: []string{"provider"}, 111 | BSCConfirmNum: 1, 112 | BSCCrossChainContractAddress: ethcmm.Address{}, 113 | }, 114 | true, 115 | }, { 116 | &ChainConfig{ 117 | BSCStartHeight: 1, 118 | BSCProviders: []string{"provider"}, 119 | BSCConfirmNum: 1, 120 | BSCCrossChainContractAddress: ethcmm.Address{1}, 121 | BBCRpcAddrs: []string{"rpc addr"}, 122 | }, 123 | true, 124 | }, { 125 | &ChainConfig{ 126 | BSCStartHeight: 1, 127 | BSCProviders: []string{"provider"}, 128 | BSCConfirmNum: 1, 129 | BSCCrossChainContractAddress: ethcmm.Address{1}, 130 | BBCRpcAddrs: []string{"rpc addr"}, 131 | BBCKeyType: "wrong", 132 | }, 133 | true, 134 | }, { 135 | &ChainConfig{ 136 | BSCStartHeight: 1, 137 | BSCProviders: []string{"provider"}, 138 | BSCConfirmNum: 1, 139 | BSCCrossChainContractAddress: ethcmm.Address{1}, 140 | BBCRpcAddrs: []string{"rpc addr"}, 141 | BBCKeyType: KeyTypeAWSMnemonic, 142 | BBCAWSRegion: "", 143 | }, 144 | true, 145 | }, { 146 | &ChainConfig{ 147 | BSCStartHeight: 1, 148 | BSCProviders: []string{"provider"}, 149 | BSCConfirmNum: 1, 150 | BSCCrossChainContractAddress: ethcmm.Address{1}, 151 | BBCRpcAddrs: []string{"rpc addr"}, 152 | BBCKeyType: KeyTypeAWSMnemonic, 153 | BBCAWSRegion: "region", 154 | BBCAWSSecretName: "", 155 | }, 156 | true, 157 | }, { 158 | &ChainConfig{ 159 | BSCStartHeight: 1, 160 | BSCProviders: []string{"provider"}, 161 | BSCConfirmNum: 1, 162 | BSCCrossChainContractAddress: ethcmm.Address{1}, 163 | BBCRpcAddrs: []string{"rpc addr"}, 164 | BBCKeyType: KeyTypeMnemonic, 165 | BBCMnemonic: "", 166 | }, 167 | true, 168 | }, { 169 | &ChainConfig{ 170 | BSCStartHeight: 1, 171 | BSCProviders: []string{"provider"}, 172 | BSCConfirmNum: 1, 173 | BSCCrossChainContractAddress: ethcmm.Address{1}, 174 | BBCRpcAddrs: []string{"rpc addr"}, 175 | BBCKeyType: KeyTypeMnemonic, 176 | BBCMnemonic: "mnemonic", 177 | RelayInterval: 0, 178 | }, 179 | true, 180 | }, { 181 | &ChainConfig{ 182 | BSCStartHeight: 1, 183 | BSCProviders: []string{"provider"}, 184 | BSCConfirmNum: 1, 185 | BSCCrossChainContractAddress: ethcmm.Address{1}, 186 | BBCRpcAddrs: []string{"rpc addr"}, 187 | BBCKeyType: KeyTypeMnemonic, 188 | BBCMnemonic: "mnemonic", 189 | RelayInterval: 1, 190 | }, 191 | false, 192 | }, 193 | } 194 | 195 | for _, config := range cases { 196 | if config.result { 197 | require.Panics(t, config.config.Validate, "the check should panic") 198 | } else { 199 | require.NotPanics(t, config.config.Validate, "the check should not panic") 200 | } 201 | } 202 | } 203 | 204 | func TestLogConfig(t *testing.T) { 205 | cases := []struct { 206 | config *LogConfig 207 | result bool 208 | }{ 209 | { 210 | &LogConfig{ 211 | UseFileLogger: true, 212 | Filename: "", 213 | }, 214 | true, 215 | }, { 216 | &LogConfig{ 217 | UseFileLogger: true, 218 | Filename: "file", 219 | MaxFileSizeInMB: 0, 220 | }, 221 | true, 222 | }, { 223 | &LogConfig{ 224 | UseFileLogger: true, 225 | Filename: "file", 226 | MaxFileSizeInMB: 1, 227 | MaxBackupsOfLogFiles: 0, 228 | }, 229 | true, 230 | }, 231 | } 232 | 233 | for _, config := range cases { 234 | if config.result { 235 | require.Panics(t, config.config.Validate, "the check should panic") 236 | } else { 237 | require.NotPanics(t, config.config.Validate, "the check should not panic") 238 | } 239 | } 240 | } 241 | -------------------------------------------------------------------------------- /util/log.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/op/go-logging" 7 | "github.com/tendermint/tendermint/libs/log" 8 | "gopkg.in/natefinch/lumberjack.v2" 9 | ) 10 | 11 | var ( 12 | // Logger instance for quick declarative logging levels 13 | Logger = logging.MustGetLogger("deputy") 14 | SdkLogger = &sdkLogger{} 15 | 16 | // log levels that are available 17 | levels = map[string]logging.Level{ 18 | "CRITICAL": logging.CRITICAL, 19 | "ERROR": logging.ERROR, 20 | "WARNING": logging.WARNING, 21 | "NOTICE": logging.NOTICE, 22 | "INFO": logging.INFO, 23 | "DEBUG": logging.DEBUG, 24 | } 25 | ) 26 | 27 | // InitLogger initialises the logger. 28 | func InitLogger(config LogConfig) { 29 | backends := make([]logging.Backend, 0) 30 | 31 | if config.UseConsoleLogger { 32 | consoleFormat := logging.MustStringFormatter(`%{time:2006-01-02 15:04:05} %{level} %{shortfunc} %{message}`) 33 | consoleLogger := logging.NewLogBackend(os.Stdout, "", 0) 34 | consoleFormatter := logging.NewBackendFormatter(consoleLogger, consoleFormat) 35 | consoleLoggerLeveled := logging.AddModuleLevel(consoleFormatter) 36 | consoleLoggerLeveled.SetLevel(levels[config.Level], "") 37 | backends = append(backends, consoleLoggerLeveled) 38 | } 39 | 40 | if config.UseFileLogger { 41 | fileLogger := logging.NewLogBackend(&lumberjack.Logger{ 42 | Filename: config.Filename, 43 | MaxSize: config.MaxFileSizeInMB, // MaxSize is the maximum size in megabytes of the log file 44 | MaxBackups: config.MaxBackupsOfLogFiles, // MaxBackups is the maximum number of old log files to retain 45 | MaxAge: config.MaxAgeToRetainLogFilesInDays, // MaxAge is the maximum number of days to retain old log files 46 | Compress: config.Compress, 47 | }, "", 0) 48 | fileFormat := logging.MustStringFormatter(`%{time:2006-01-02 15:04:05} %{level} %{shortfunc} %{message}`) 49 | fileFormatter := logging.NewBackendFormatter(fileLogger, fileFormat) 50 | fileLoggerLeveled := logging.AddModuleLevel(fileFormatter) 51 | fileLoggerLeveled.SetLevel(levels[config.Level], "") 52 | backends = append(backends, fileLoggerLeveled) 53 | } 54 | 55 | logging.SetBackend(backends...) 56 | } 57 | 58 | type sdkLogger struct { 59 | } 60 | 61 | func (l *sdkLogger) Debug(msg string, keyvals ...interface{}) { 62 | Logger.Debug(msg) 63 | } 64 | 65 | func (l *sdkLogger) Info(msg string, keyvals ...interface{}) { 66 | Logger.Info(msg) 67 | } 68 | 69 | func (l *sdkLogger) Error(msg string, keyvals ...interface{}) { 70 | Logger.Error(msg) 71 | } 72 | 73 | func (l *sdkLogger) With(keyvals ...interface{}) log.Logger { 74 | return l 75 | } 76 | -------------------------------------------------------------------------------- /util/test_util.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "io/ioutil" 5 | 6 | "github.com/jinzhu/gorm" 7 | 8 | "github.com/binance-chain/oracle-relayer/model" 9 | ) 10 | 11 | var testConfig = ` 12 | { 13 | "db_config": { 14 | "dialect": "sqlite3", 15 | "db_path": "user:password@(host:port)/db_name?charset=utf8&parseTime=True&loc=Local" 16 | }, 17 | "chain_config": { 18 | "bsc_start_height": 1, 19 | "bsc_providers": ["bsc_provider"], 20 | "bsc_confirm_num": 2, 21 | "bsc_cross_chain_contract_address": "0x0000000000000000000000000000000000001004", 22 | 23 | "bbc_rpc_addrs": ["bbc_rpc_addr"], 24 | "bbc_key_type": "mnemonic", 25 | "bbc_aws_region": "", 26 | "bbc_aws_secret_name": "", 27 | "bbc_mnemonic": "", 28 | 29 | "relay_interval": 1000 30 | }, 31 | "log_config": { 32 | "level": "INFO", 33 | "filename": "", 34 | "max_file_size_in_mb": 0, 35 | "max_backups_of_log_files": 0, 36 | "max_age_to_retain_log_files_in_days": 0, 37 | "use_console_logger": true, 38 | "use_file_logger": false, 39 | "compress": false 40 | }, 41 | "admin_config": { 42 | "listen_addr": ":8080" 43 | }, 44 | "alert_config": { 45 | "moniker": "moniker", 46 | "telegram_bot_id": "your_bot_id", 47 | "telegram_chat_id": "your_chat_id", 48 | "block_update_time_out": 60 49 | } 50 | } 51 | ` 52 | 53 | func GetTestConfig() *Config { 54 | config := ParseConfigFromJson(testConfig) 55 | return config 56 | } 57 | 58 | func PrepareDB(config *Config) (*gorm.DB, error) { 59 | config.DBConfig.DBPath = "tmp.db" 60 | tmpDBFile, err := ioutil.TempFile("", config.DBConfig.DBPath) 61 | if err != nil { 62 | return nil, err 63 | } 64 | 65 | db, err := gorm.Open(config.DBConfig.Dialect, tmpDBFile.Name()) 66 | if err != nil { 67 | return nil, err 68 | } 69 | model.InitTables(db) 70 | return db, nil 71 | } 72 | -------------------------------------------------------------------------------- /util/util.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "net/http" 7 | "net/url" 8 | 9 | "github.com/PagerDuty/go-pagerduty" 10 | ) 11 | 12 | const ( 13 | IncidentDedupKeyBlockTimeout = "block_timeout" 14 | IncidentDedupKeyRelayError = "relay_error" 15 | ) 16 | 17 | var tgAlerter TgAlerter 18 | 19 | var pagerDutyAuthToken = "" 20 | 21 | type TgAlerter struct { 22 | BotId string 23 | ChatId string 24 | } 25 | 26 | func InitAlert(cfg *AlertConfig) { 27 | tgAlerter = TgAlerter{ 28 | BotId: cfg.TelegramBotId, 29 | ChatId: cfg.TelegramChatId, 30 | } 31 | 32 | pagerDutyAuthToken = cfg.PagerDutyAuthToken 33 | } 34 | 35 | // SendTelegramMessage sends message to telegram group 36 | func SendTelegramMessage(msg string) { 37 | if tgAlerter.BotId == "" || tgAlerter.ChatId == "" || msg == "" { 38 | return 39 | } 40 | 41 | endPoint := fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage", tgAlerter.BotId) 42 | formData := url.Values{ 43 | "chat_id": {tgAlerter.ChatId}, 44 | "parse_mode": {"html"}, 45 | "text": {msg}, 46 | } 47 | Logger.Infof("send tg message, bot_id=%s, chat_id=%s, msg=%s", tgAlerter.BotId, tgAlerter.ChatId, msg) 48 | res, err := http.PostForm(endPoint, formData) 49 | if err != nil { 50 | Logger.Errorf("send telegram message error, bot_id=%s, chat_id=%s, msg=%s, err=%s", tgAlerter.BotId, tgAlerter.ChatId, msg, err.Error()) 51 | return 52 | } 53 | 54 | bodyBytes, err := ioutil.ReadAll(res.Body) 55 | defer res.Body.Close() 56 | if err != nil { 57 | Logger.Errorf("read http response error, err=%s", err.Error()) 58 | return 59 | } 60 | Logger.Infof("tg response: %s", string(bodyBytes)) 61 | } 62 | 63 | func SendPagerDutyAlert(detail string, dedupKey string) { 64 | if pagerDutyAuthToken == "" { 65 | return 66 | } 67 | 68 | event := pagerduty.V2Event{ 69 | RoutingKey: pagerDutyAuthToken, 70 | Action: "trigger", 71 | DedupKey: dedupKey, 72 | Payload: &pagerduty.V2Payload{ 73 | Summary: "oracle relayer error detected, please contact Zhenxing (13041017167), Haoyang (15618304832), or Fudong (13732255759)", 74 | Source: "sdk", 75 | Severity: "error", 76 | Component: "oracle_relayer", 77 | Group: "dex", 78 | Class: "oracle_relayer", 79 | Details: detail, 80 | }, 81 | } 82 | _, err := pagerduty.ManageEvent(event) 83 | if err != nil { 84 | Logger.Errorf("send pager duty alert error, err=%s", err.Error()) 85 | } 86 | } 87 | --------------------------------------------------------------------------------